mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 16:35:02 +01:00

Subject: apparmor-utils: Translation unification References: bnc#586072 This patch removes small inconsistencies between identical strings to allow for easier translation. Reported-by: Isis Binder <isis.binder@gmail.com> Signed-off-by: Jeff Mahoney <jeffm@suse.com> Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2025 lines
57 KiB
Perl
Executable file
2025 lines
57 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 DBI;
|
|
use DBD::SQLite;
|
|
use Locale::gettext;
|
|
use POSIX;
|
|
use ycp;
|
|
|
|
setlocale(LC_MESSAGES, "");
|
|
textdomain("Reports");
|
|
|
|
my $eventDb = '/var/log/apparmor/events.db';
|
|
my $numEvents = 1000;
|
|
|
|
sub YcpDebug ($$) {
|
|
|
|
my $argList = "";
|
|
#my ($script, $args) = @_;
|
|
my $script = shift;
|
|
my $args = shift;
|
|
|
|
if ($args && ref($args) eq "HASH") {
|
|
|
|
for (sort keys(%$args) ) {
|
|
$argList .= "$_ is ..$args->{$_}.., " if $args->{$_};
|
|
}
|
|
|
|
} elsif ($args && ref($args) eq "ARRAY") {
|
|
|
|
for my $row (@$args) {
|
|
for (sort keys(%$row) ) {
|
|
$argList .= "$_ is ..$row->{$_}.., " if $row->{$_};
|
|
}
|
|
}
|
|
} elsif ( $args ) {
|
|
$argList = $args;
|
|
} else {
|
|
my $prob = ref($args);
|
|
$argList = "Type not supported for printing debug: $prob";
|
|
}
|
|
|
|
ycp::y2milestone("[apparmor $script] vars: $argList");
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
sub getNetSockList {
|
|
|
|
my @netsockList = (
|
|
"All", "UNIX Domain Sockets", "IP v4", "Radio AX.25",
|
|
"Novell IPX", "Appletalk", "Radio NET/ROM",
|
|
"Multiprotocol Bridge", "ATM PVC", "X.25", "IP v6",
|
|
"Radio X.25 PLP", "DECnet", "NetBEUI", "Security Callback",
|
|
"PF Key Management", "Netlink", "Packet", "Ash",
|
|
"Econet", "ATM SVC", "Filler (Ignore)", "Linux SNA",
|
|
"IRDA", "PPPoX", "Wanpipe", "Linux LLC",
|
|
"Filler (Ignore)", "Filler (Ignore)", "Filler (Ignore)", "TIPC",
|
|
"Bluetooth", "IUCV", "RxRPC"
|
|
);
|
|
|
|
|
|
return \@netsockList;
|
|
}
|
|
|
|
sub netsock_name2num {
|
|
|
|
my $sockName = shift;
|
|
my $sockNum = '';
|
|
my $netsockList = getNetSockList();
|
|
|
|
my $i;
|
|
|
|
for ($i = 0; $i < @$netsockList; $i++) {
|
|
last if $_ eq @$netsockList[$i];
|
|
}
|
|
|
|
if ($i < @$netsockList) {
|
|
$sockNum = $i;
|
|
}
|
|
|
|
return $sockNum;
|
|
}
|
|
|
|
sub netsock_num2name {
|
|
|
|
my $sockNum = shift;
|
|
my $sockName = undef;
|
|
my $netsockList = getNetSockList();
|
|
|
|
if ( $sockNum < @$netsockList) {
|
|
$sockName = @$netsockList[$sockNum];
|
|
}
|
|
|
|
if ( $sockName eq "Filler (Ignore)" ) {
|
|
$sockName = undef;
|
|
}
|
|
|
|
return $sockName;
|
|
}
|
|
|
|
sub matchFailed ($$) {
|
|
|
|
my $args = shift;
|
|
my $rec = shift;
|
|
|
|
# Check filters
|
|
if ($args->{'pid'} && $args->{'pid'} ne '-') {
|
|
return(1) unless ($args->{'pid'} eq $rec->{'pid'});
|
|
}
|
|
if ( $args->{'severity'}
|
|
&& $args->{'severity'} ne "00"
|
|
&& $args->{'severity'} ne '-')
|
|
{
|
|
if ($args->{'severity'} eq "U") { $args->{'severity'} = '-1'; }
|
|
return(1) unless ($args->{'severity'} eq $rec->{'severity'});
|
|
}
|
|
if ($args->{'mode_deny'} && $args->{'mode_deny'} ne '-') {
|
|
return(1) unless ($args->{'mode_deny'} eq $rec->{'mode_deny'});
|
|
}
|
|
if ($args->{'mode_req'} && $args->{'mode_req'} ne '-') {
|
|
return(1) unless ($args->{'mode_req'} eq $rec->{'mode_req'});
|
|
}
|
|
|
|
if ($args->{'resource'} && $args->{'resource'} ne '-') {
|
|
return(1) unless ($args->{'resource'} eq $rec->{'resource'});
|
|
}
|
|
if ($args->{'sdmode'} && $args->{'sdmode'} ne '-') {
|
|
# Needs reversal of comparison for sdmode
|
|
return(1) unless ($rec->{'sdmode'} =~ /$args->{'sdmode'}/);
|
|
}
|
|
if ($args->{'op'} && $args->{'op'} ne '-') {
|
|
return(1) unless ($args->{'op'} eq $rec->{'op'});
|
|
}
|
|
if ($args->{'attr'} && $args->{'attr'} ne '-') {
|
|
return(1) unless ($args->{'attr'} eq $rec->{'attr'});
|
|
}
|
|
if ($args->{'name_alt'} && $args->{'name_alt'} ne '-') {
|
|
return(1) unless ($args->{'name_alt'} eq $rec->{'name_alt'});
|
|
}
|
|
if ($args->{'net_family'} && $args->{'net_family'} ne '-') {
|
|
return(1) unless ($args->{'net_family'} eq $rec->{'net_family'});
|
|
}
|
|
if ($args->{'net_proto'} && $args->{'net_proto'} ne '-') {
|
|
return(1) unless ($args->{'net_proto'} eq $rec->{'net_proto'});
|
|
}
|
|
if ($args->{'net_socktype'} && $args->{'net_socktype'} ne '-') {
|
|
return(1) unless ($args->{'net_socktype'} eq $rec->{'net_socktype'});
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
# Translate mode & sdmode for parsing
|
|
sub rewriteModes {
|
|
my $filts = shift;
|
|
|
|
# Mode wrangling - Rewrite for better matches
|
|
for ('mode_req','mode_deny') {
|
|
|
|
if ($filts->{$_} && $filts->{$_} ne "All") {
|
|
|
|
my @mode = ();
|
|
my $tmpMode = undef;
|
|
|
|
@mode = split(//, $filts->{$_});
|
|
|
|
if (@mode > 0) {
|
|
#$tmpMode = join("|", @mode);
|
|
$tmpMode = join("", @mode);
|
|
if ($tmpMode =~ /m/) {
|
|
$tmpMode =~ s/m//g;
|
|
$tmpMode = "m" . $tmpMode;
|
|
}
|
|
} else {
|
|
delete($filts->{$_});
|
|
}
|
|
|
|
if ($tmpMode) {
|
|
$filts->{$_} = $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 getFilterList ($) {
|
|
|
|
my $args = shift;
|
|
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_req'}) { $filts->{'mode_req'} = $args->{'mode_req'}; }
|
|
if ($args->{'mode_deny'}) { $filts->{'mode_deny'} = $args->{'mode_deny'}; }
|
|
if ($args->{'op'}) { $filts->{'op'} = $args->{'op'}; }
|
|
if ($args->{'attr'}) { $filts->{'attr'} = $args->{'attr'}; }
|
|
if ($args->{'name_alt'}) { $filts->{'name_alt'} = $args->{'name_alt'}; }
|
|
if ($args->{'net_family'}) { $filts->{'net_family'} = $args->{'net_family'}; }
|
|
if ($args->{'net_proto'}) { $filts->{'net_proto'} = $args->{'net_proto'}; }
|
|
if ($args->{'net_socktype'}) { $filts->{'net_socktype'} = $args->{'net_socktype'}; }
|
|
|
|
for (sort(keys(%$filts))) {
|
|
if ($filts->{$_} eq '-' || $filts->{$_} eq 'All') {
|
|
delete($filts->{$_});
|
|
}
|
|
}
|
|
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 ($@) {
|
|
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_req'}) {
|
|
$args->{'mode_req'} =~ s/\&//g;
|
|
$args->{'mode_req'} =~ s/Mode\://g;
|
|
$args->{'mode_req'} =~ s/\s//g;
|
|
|
|
if ($args->{'mode_req'} eq "All") {
|
|
delete($args->{'mode_req'});
|
|
}
|
|
}
|
|
########################################
|
|
|
|
$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 = getFilterList($args);
|
|
my $midQuery = getQueryFilters($filts, $start, $end);
|
|
if ($midQuery) { $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 ($@) {
|
|
ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
|
|
return;
|
|
}
|
|
|
|
$dbh->disconnect();
|
|
|
|
$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 {
|
|
ycp::y2error(sprintf(gettext("Couldn't open file: %s."), $eventRep));
|
|
}
|
|
|
|
} else {
|
|
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 ($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")) {
|
|
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";
|
|
|
|
print LOG "$repName: Log generated by Novell AppArmor, $date\n\n";
|
|
printf LOG "%-21s%-32s%-8s%-51s", "Host", "Date", "Program", "Profile",
|
|
"PID", "Severity", "Mode Deny", "Mode Request","Detail", "Access Type",
|
|
"Operation", "Attribute", "Additional Name", "Parent", "Active Hat",
|
|
"Net Family", "Net Protocol", "Net Socket Type";
|
|
|
|
print LOG "\n";
|
|
|
|
for (sort (@$db)) {
|
|
print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},";
|
|
print LOG "$_->{'pid'},$_->{'severity'},$_->{'mode_deny'},$_->{'mode_req'},";
|
|
print LOG "$_->{'resource'},$_->{'sdmode'},$_->{'op'},$_->{'attr'},";
|
|
print LOG "$_->{'name_alt'},$_->{'parent'},$_->{'active_hat'},";
|
|
print LOG "$_->{'net_family'},$_->{'net_proto'},$_->{'net_socktype'}\n";
|
|
}
|
|
|
|
close LOG;
|
|
}
|
|
|
|
sub exportLog {
|
|
|
|
my ($exportLog, $db, $header) = @_;
|
|
|
|
return unless $db;
|
|
|
|
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) {
|
|
print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},";
|
|
print LOG "$_->{'pid'},$_->{'severity'},$_->{'mode_deny'},$_->{'mode_req'},";
|
|
print LOG "$_->{'resource'},$_->{'sdmode'},$_->{'op'},$_->{'attr'},";
|
|
print LOG "$_->{'name_alt'},$_->{'parent'},$_->{'active_hat'},";
|
|
print LOG "$_->{'net_family'},$_->{'net_proto'},$_->{'net_socktype'}\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>Host</th><th>Date</th><th>Program</th>" .
|
|
"<th>Profile</th><th>PID</th><th>Severity</th><th>Mode Deny</th>" .
|
|
"<th>Mode Request</th><th>Detail</th><th>Access Type</th><th>Operation</th>" .
|
|
"<th>Attribute</th><th>Additional Name</th><th>Parent</th><th>Active Hat</th>" .
|
|
"<th>Net Family</th><th>Net Protocol</th><th>Net Socket Type</th></tr>\n";
|
|
|
|
my $idx = 1;
|
|
|
|
for (@$db) {
|
|
$idx++;
|
|
|
|
my $logLine =
|
|
"<td> $_->{'date'} </td>"
|
|
. "<td> $_->{'prog'} </td>"
|
|
. "<td> $_->{'profile'} </td>"
|
|
. "<td> $_->{'pid'} </td>"
|
|
. "<td> $_->{'severity'} </td>"
|
|
. "<td> $_->{'mode_deny'} </td>"
|
|
. "<td> $_->{'mode_req'} </td>"
|
|
. "<td> $_->{'resource'} </td>"
|
|
. "<td> $_->{'sdmode'} </td>"
|
|
. "<td> $_->{'op'} </td>"
|
|
. "<td> $_->{'attr'} </td>"
|
|
. "<td> $_->{'name_alt'} </td>"
|
|
. "<td> $_->{'parent'} </td>"
|
|
. "<td> $_->{'active_hat'} </td>"
|
|
. "<td> $_->{'net_family'} </td>"
|
|
. "<td> $_->{'net_proto'} </td>"
|
|
. "<td> $_->{'net_socktype'} </td></tr>";
|
|
|
|
my $plainCell = "<tr><td> $_->{'host'} </td>";
|
|
my $shadedCell = "<tr='edefef'><td> $_->{'host'} </td>";
|
|
my $logLinePlain = $plainCell . $logLine;
|
|
my $logLineShaded = $shadedCell . $logLine;
|
|
|
|
if ($idx % 2 == 0) {
|
|
print LOG "$logLinePlain\n";
|
|
} else {
|
|
# Shade every other row
|
|
print LOG "$logLineShaded\n";
|
|
}
|
|
}
|
|
|
|
print LOG "<br></table></font></body></html>\n\n";
|
|
}
|
|
|
|
close LOG;
|
|
} else {
|
|
ycp::y2error(sprintf(gettext("Export Log Error: Couldn't open %s"), $exportLog));
|
|
}
|
|
|
|
}
|
|
|
|
# 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) {
|
|
ycp::y2error(gettext("Fatal error. No report name given. Exiting."));
|
|
}
|
|
|
|
if (!$repConf || !-e $repConf) {
|
|
$repConf = '/etc/apparmor/reports.conf';
|
|
if (!-e $repConf) {
|
|
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\>/) {
|
|
|
|
/\<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 {
|
|
ycp::y2error(sprintf(gettext("Failed to parse: %s."), $_));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
close XML;
|
|
|
|
} else {
|
|
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);
|
|
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) {
|
|
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";
|
|
|
|
# Get list of hosts to scan
|
|
if (opendir(TDIR, $targetDir)) {
|
|
|
|
@hostList = grep(/\.db/, readdir(TDIR));
|
|
close TDIR;
|
|
|
|
} else {
|
|
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 $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 ($@) {
|
|
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 ($@) {
|
|
ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
|
|
return;
|
|
}
|
|
|
|
# Get rest of stats
|
|
eval { $ret = $dbh->selectall_arrayref("$query"); };
|
|
|
|
if ($@) {
|
|
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'}) {
|
|
|
|
# Find earliest start date
|
|
if ($_->{'startdate'} > $ev->{'date'}) {
|
|
$_->{'startdate'} = $ev->{'date'};
|
|
}
|
|
|
|
# tally all events reported for host
|
|
$_->{'numEvents'}++;
|
|
|
|
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
|
|
for my $filt (%$rep) {
|
|
next unless $filt && $rep->{$filt};
|
|
$rep->{$filt} =~ s/\s+//g; # repname won't be in here, so no spaces
|
|
if ( $rep->{$filt} eq "-" || $rep->{$filt} eq 'All' ||
|
|
$rep->{$filt} eq '*' )
|
|
{
|
|
delete($rep->{$filt});
|
|
}
|
|
}
|
|
|
|
$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
|
|
############################################################
|
|
for my $key(keys(%$filts)) {
|
|
|
|
# Special case for severity
|
|
if ( $key eq 'severity' ) {
|
|
|
|
if ($filts->{$key} eq "-" || $filts->{$key} eq "All") {
|
|
delete($filts->{$key});
|
|
} elsif ($filts->{$key} eq "-1"
|
|
|| $filts->{$key} 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->{$key}\' ";
|
|
} else {
|
|
$query .= "WHERE events.severity >= \'$filts->{$key}\' ";
|
|
}
|
|
$wFlag = 1;
|
|
}
|
|
|
|
# Special case for sdmode
|
|
} elsif ($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;
|
|
|
|
# All other filters
|
|
} elsif ($wFlag == 0) {
|
|
$query .= "WHERE events.$key LIKE \'\%$filts->{$key}\%\' ";
|
|
$wFlag = 1;
|
|
} else {
|
|
$query .= "AND events.$key LIKE \'\%$filts->{$key}\%\' ";
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
if ($midQuery) { $query .= "$midQuery"; }
|
|
}
|
|
|
|
# Finish query
|
|
$query .= "Order by $sortKey LIMIT $limit,$numEvents";
|
|
|
|
return $query;
|
|
}
|
|
|
|
# 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_req'} = $args->{'mode'}; }
|
|
if ($args->{'mode_req'}) { $filts->{'mode_req'} = $args->{'mode_req'}; }
|
|
if ($args->{'mode_deny'}) { $filts->{'mode_deny'} = $args->{'mode_deny'}; }
|
|
|
|
}
|
|
|
|
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_req'}
|
|
&& ($filts->{'mode_req'} eq "-" || $filts->{'mode_req'} eq "All"))
|
|
{
|
|
delete($filts->{'mode_req'});
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
# 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, op, pid, sdmode, type, mode_deny, mode_req,
|
|
# resource, target, profile, prog, name_alt, attr, parent, active_hat,
|
|
# net_family, net_proto, net_socktype, 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 ($@) {
|
|
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->{'op'},$rec->{'pid'},
|
|
$rec->{'sdmode'},$rec->{'type'},$rec->{'mode_deny'},$rec->{'mode_req'},
|
|
$rec->{'resource'},$rec->{'target'},$rec->{'profile'}, $rec->{'prog'},
|
|
$rec->{'name_alt'},$rec->{'attr'},$rec->{'parent'},$rec->{'active_hat'},
|
|
$rec->{'net_family'},$rec->{'net_proto'},$rec->{'net_socktype'},
|
|
$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->{'logFile'}) {
|
|
$eventRep = $args->{'logFile'};
|
|
}
|
|
|
|
if (open(REP, "<$eventRep")) {
|
|
|
|
my $page = 1;
|
|
|
|
if ($args->{'page'}) { $page = $args->{'page'}; }
|
|
|
|
my $id = 1;
|
|
my $slurp = 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/) {
|
|
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_deny'},$db->{'mode_req'},
|
|
$db->{'resource'},$db->{'sdmode'},$db->{'op'},$db->{'attr'},
|
|
$db->{'name_alt'},$db->{'parent'},$db->{'active_hat'},
|
|
$db->{'net_family'},$db->{'net_proto'},$db->{'net_socktype'})
|
|
= 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 {
|
|
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 $eventRep = "/var/log/apparmor/reports/events.rpt";
|
|
|
|
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'},$_->{'time'},$_->{'prog'},$_->{'profile'},";
|
|
print REP "$_->{'pid'},$_->{'severity'},$_->{'mode_deny'},$_->{'mode_req'},";
|
|
print REP "$_->{'resource'},$_->{'sdmode'},$_->{'op'},$_->{'attr'},";
|
|
print REP "$_->{'name_alt'},$_->{'parent'},$_->{'active_hat'},";
|
|
print REP "$_->{'net_family'},$_->{'net_proto'},$_->{'net_socktype'}\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 here
|
|
|
|
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")) {
|
|
|
|
$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_req'}
|
|
&& ($args->{'mode_req'} =~ /All/ || $args->{'mode_req'} =~ /^\s*-\s*$/))
|
|
{
|
|
delete($args->{'mode_req'});
|
|
}
|
|
if ($args->{'mode_deny'}
|
|
&& ($args->{'mode_deny'} =~ /All/ || $args->{'mode_deny'} =~ /^\s*-\s*$/))
|
|
{
|
|
delete($args->{'mode_deny'});
|
|
}
|
|
|
|
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->{'severity'}
|
|
&& ($args->{'severity'} =~ /All/ || $args->{'severity'} =~ /^\s*-\s*$/))
|
|
{
|
|
delete($args->{'severity'});
|
|
}
|
|
|
|
my $regExp = 'prog|profile|pid|resource|mode|severity|date|op|target|attr|net_|name_alt';
|
|
|
|
# get list of keys
|
|
my @keyList = keys(%$args);
|
|
|
|
# find filters in @keyList
|
|
if ( grep(/$regExp/, @keyList) == 1 ) {
|
|
$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->{'severity'},$rec->{'mode_deny'},$rec->{'mode_req'},
|
|
$rec->{'resource'},$rec->{'sdmode'},$rec->{'op'},$rec->{'attr'},
|
|
$rec->{'name_alt'},$rec->{'parent'},$rec->{'active_hat'},
|
|
$rec->{'net_family'},$rec->{'net_proto'},$rec->{'net_socktype'})
|
|
= split(/\,/, $_);
|
|
|
|
|
|
# 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
|
|
next if matchFailed($args,$rec);
|
|
|
|
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 $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_req'}
|
|
&& ($args->{'mode_req'} =~ /All/ || $args->{'mode_req'} =~ /^\s*-\s*$/))
|
|
{
|
|
delete($args->{'mode_req'});
|
|
}
|
|
|
|
if ($args->{'mode_deny'}
|
|
&& ($args->{'mode_deny'} =~ /All/ || $args->{'mode_deny'} =~ /^\s*-\s*$/))
|
|
{
|
|
delete($args->{'mode_deny'});
|
|
}
|
|
|
|
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->{'severity'}
|
|
&& ($args->{'severity'} =~ /All/ || $args->{'severity'} =~ /^\s*-\s*$/))
|
|
{
|
|
delete($args->{'severity'});
|
|
}
|
|
|
|
$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'}/; }
|
|
|
|
($rec->{'host'}, $rec->{'time'}, $rec->{'prog'}, $rec->{'profile'},
|
|
$rec->{'pid'}, $rec->{'severity'}, $rec->{'mode_req'}, $rec->{'resource'},
|
|
$rec->{'sdmode'}) = split(/\,/, $_);
|
|
|
|
# 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
|
|
next if matchFailed($args,$rec);
|
|
|
|
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?
|
|
}
|
|
}
|
|
|
|
$error = writeEventReport(\@db, $args);
|
|
|
|
} else {
|
|
$error = "Couldn't open $eventRep.";
|
|
}
|
|
|
|
return $error;
|
|
}
|
|
|
|
1;
|
|
|