1
# $Id: Reports.pm 462 2007-03-21 17:49:39Z jmichael-at-suse-de $
2
# ------------------------------------------------------------------
4
# Copyright (C) 2005-2006 Novell/SUSE
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of version 2 of the GNU General Public
8
# License published by the Free Software Foundation.
10
# ------------------------------------------------------------------
12
package Immunix::Reports;
14
################################################################################
15
# /usr/lib/perl5/site_perl/Reports.pm
17
# - Parses /var/log/messages for SubDomain messages
18
# - Writes results to .html or comma-delimited (.csv) files (Optional)
22
# Time::Local (temporary)
25
# -Start Date|End Date (Month, Day, Year, Time)
31
################################################################################
41
setlocale(LC_MESSAGES, "");
42
textdomain("Reports");
44
my $eventDb = '/var/log/apparmor/events.db';
64
my $numMonth = $months->{$lexMon};
72
my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
73
my $lexMonth = $months[ ($monthNum - 1) ];
78
# Converts Epoch Time to Formatted Date String
82
my $date = localtime($epTime);
84
my ($day, $mon, $mondate, $time, $year) = split(/\s+/, $date);
85
my ($hour, $min, $sec) = split(/:/, $time);
87
$mon = month2Num($mon);
89
# we want 2 digits for easier reading
90
$mon = sprintf("%02d", $mon);
91
$mondate = sprintf("%02d", $mondate);
93
my $newDate = "$year-$mon-$mondate $time";
99
$num = sprintf("%.2f", $num);
106
my $pnum = int($num);
115
sub checkFileExists {
118
if ($file && -e $file) {
125
# Translate mode & sdmode for parsing
129
# Mode wrangling - Rewrite for better matches
130
if ($filts->{'mode'} && $filts->{'mode'} ne "All") {
135
@mode = split(//, $filts->{'mode'});
138
$tmpMode = join("|", @mode);
140
delete($filts->{'mode'});
144
$filts->{'mode'} = $tmpMode;
148
# Rewrite sdmode for more flexible matches
149
if ($filts->{'sdmode'} && $filts->{'sdmode'} ne "All") {
151
if ($filts->{'sdmode'} =~ /[pP]/) { push(@tmpMode, 'PERMIT'); }
152
if ($filts->{'sdmode'} =~ /[rR]/) { push(@tmpMode, 'REJECT'); }
153
if ($filts->{'sdmode'} =~ /[aA]/) { push(@tmpMode, 'AUDIT'); }
155
$filts->{'sdmode'} = join('|', @tmpMode);
157
delete($filts->{'sdmode'});
166
# make sure the eventd is enabled before we do any reports
168
if (open(SDCONF, "/etc/apparmor/subdomain.conf")) {
170
if (/^\s*APPARMOR_ENABLE_AAEVENTD\s*=\s*(\S+)\s*$/) {
173
# strip quotes from the value if present
174
$flag = $1 if $flag =~ /^"(\S+)"$/;
175
$need_enable = 1 if $flag ne "yes";
181
# if the eventd isn't enabled, we'll turn it on the first time they
182
# run a report and start it up - if something fails for some reason,
183
# we should just fall through and the db check should correctly tell
184
# the caller that the db isn't initialized correctly
186
my $old = "/etc/apparmor/subdomain.conf";
187
my $new = "/etc/apparmor/subdomain.conf.$$";
188
if (open(SDCONF, $old)) {
189
if (open(SDCONFNEW, ">$new")) {
193
if (/^\s*APPARMOR_ENABLE_AAEVENTD\s*=/) {
194
print SDCONFNEW "APPARMOR_ENABLE_AAEVENTD=\"yes\"\n";
203
print SDCONFNEW "APPARMOR_ENABLE_AAEVENTD=\"yes\"\n";
208
# if we were able to overwrite the old config
209
# config file with the new stuff, we'll kick
210
# the init script to start up aa-eventd
211
if (rename($new, $old)) {
212
if (-e "/sbin/rcaaeventd") {
213
system("/sbin/rcaaeventd restart >/dev/null 2>&1");
215
system("/sbin/rcapparmor restart >/dev/null 2>&1");
227
# Check that events db exists and is populated
228
# - Returns 1 for good db, 0 for bad db
231
my $eventDb = '/var/log/apparmor/events.db';
233
# make sure the event daemon is enabled
234
if (enableEventD()) {
238
# wait until the event db appears or we hit 1 min
239
while (!-e $eventDb) {
241
return 0 if ((time - $now) >= 60);
244
# wait until it stops changing or we hit 1 min - the event
245
# daemon flushes events to the db every five seconds.
246
my $last_modified = 0;
247
my $modified = (stat($eventDb))[9];
248
while ($last_modified != $modified) {
250
last if ((time - $now) >= 60);
251
$last_modified = $modified;
252
$modified = (stat($eventDb))[9];
256
my $query = "SELECT count(*) FROM events ";
259
my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 });
262
my $sth = $dbh->prepare($query);
264
$count = $sth->fetchrow_array();
270
ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
276
if ($count && $count > 0) {
283
# Called from ag_reports_parse
291
my $eventRep = "/var/log/apparmor/reports/events.rpt";
293
# Figure out whether we want db count or file parse
294
if ($args->{'type'}) {
295
if ($args->{'type'} eq 'sir' || $args->{'type'} eq 'ess-multi') {
297
} elsif ($args->{'type'} eq 'ess') {
298
return 1; # ess reports have one page by definition
300
$type = 'arch'; # archived or file
304
# Parse sdmode & mode labels
305
if ($args->{'sdmode'}) {
306
$args->{'sdmode'} =~ s/\&//g;
307
$args->{'sdmode'} =~ s/\://g;
308
$args->{'sdmode'} =~ s/\s//g;
309
$args->{'sdmode'} =~ s/AccessType//g;
311
if ($args->{'sdmode'} eq "All") {
312
delete($args->{'sdmode'});
316
if ($args->{'mode'}) {
317
$args->{'mode'} =~ s/\&//g;
318
$args->{'mode'} =~ s/Mode\://g;
319
$args->{'mode'} =~ s/\s//g;
321
if ($args->{'mode'} eq "All") {
322
delete($args->{'mode'});
325
########################################
327
$args = rewriteModes($args);
329
if ($type && $type eq 'db') {
334
if ($args->{'startTime'} && $args->{'startTime'} > 0) {
335
$start = $args->{'startTime'};
338
if ($args->{'endTime'} && $args->{'endTime'} > 0) {
339
$end = $args->{'endTime'};
342
my $query = "SELECT count(*) FROM events ";
344
# We need filter information for getting a correct count
345
#my $filts = getSirFilters($args); # these should be sent from YaST
348
if ($args->{'prog'}) { $filts->{'prog'} = $args->{'prog'}; }
349
if ($args->{'profile'}) { $filts->{'profile'} = $args->{'profile'}; }
350
if ($args->{'pid'}) { $filts->{'pid'} = $args->{'pid'}; }
351
if ($args->{'resource'}) { $filts->{'resource'} = $args->{'resource'}; }
352
if ($args->{'severity'}) { $filts->{'severity'} = $args->{'severity'}; }
353
if ($args->{'sdmode'}) { $filts->{'sdmode'} = $args->{'sdmode'}; }
354
if ($args->{'mode'}) { $filts->{'mode'} = $args->{'mode'}; }
356
for (sort(keys(%$filts))) {
357
if ($filts->{$_} eq '-' || $filts->{$_} eq 'All') {
358
delete($filts->{$_});
362
my $midQuery = getQueryFilters($filts, $start, $end);
364
$query .= "$midQuery";
367
my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 });
370
my $sth = $dbh->prepare($query);
372
$count = $sth->fetchrow_array();
378
ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
384
#ycp::y2milestone("Numpages Query: $query"); # debug
386
$numPages = pageRound($count / $numEvents);
387
if ($numPages < 1) { $numPages = 1; }
389
} elsif ($type && $type eq 'arch') {
391
if (open(REP, "<$eventRep")) {
404
ycp::y2error(sprintf(gettext("Couldn't open file: %s."), $eventRep));
408
ycp::y2error(gettext("No type value passed. Unable to determine page count."));
412
if ($numPages < 1) { $numPages = 1; }
414
my $numCheck = int($count / $numEvents);
416
if ($numPages < $numCheck) {
417
$numPages = $numCheck;
423
sub getEpochFromNum {
425
my $place = shift || undef; # Used to set default $sec if undef
427
my ($numMonth, $numDay, $time, $year) = split(/\s+/, $date);
428
my ($hour, $min, $sec) = '0';
432
($hour, $min, $sec, $junk) = split(/\:/, $time);
433
if (!$hour || $hour eq "") { $hour = '0'; }
434
if (!$min || $min eq "") { $min = '0'; }
435
if (!$sec || $sec eq "") {
436
if ($place eq 'end') {
444
$numMonth--; # Months start from 0 for epoch translation
446
if (!$year) { $year = (split(/\s+/, localtime))[4]; }
447
my $epochDate = timelocal($sec, $min, $hour, $numDay, $numMonth, $year);
452
sub getEpochFromStr {
455
my ($lexMonth, $dateDay, $fullTime, $year) = split(/\s+/, $lexDate);
457
#my ($lexDay, $lexMonth, $dateDay, $fullTime, $year) = split(/\s+/, $lexDate);
458
my ($hour, $min, $sec) = split(/\:/, $fullTime);
460
if (!$year) { $year = (split(/\s+/, localtime))[4]; }
462
my $numMonth = month2Num($lexMonth);
464
my $epochDate = timelocal($sec, $min, $hour, $dateDay, $numMonth, $year);
469
# Replaces old files with new files
471
my ($oldFile, $newFile) = @_;
473
if (unlink("$oldFile")) {
474
if (!rename("$newFile", "$oldFile")) {
475
if (!system('/bin/mv', "$newFile", "$oldFile")) {
476
ycp::y2error(sprintf(gettext("Failed copying %s."), $oldFile));
481
system('/bin/rm', "$oldFile");
482
system('/bin/mv', "$newFile", "$oldFile");
488
# This is a holder, that was originally part of exportLog()
489
# Used by /usr/bin/reportgen.pl
490
sub exportFormattedText {
491
my ($repName, $logFile, $db) = @_;
493
my $date = localtime;
494
open(LOG, ">$logFile") || die "Couldn't open $logFile";
496
# Date Profile PID Mesg
497
print LOG "$repName: Log generated by Novell AppArmor, $date\n\n";
498
printf LOG "%-21s%-32s%-8s%-51s", "Host", "Date", "Program", "Profile", "PID", "Severity", "Mode", "Detail", "Access Type";
502
print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},";
503
print LOG "$_->{'pid'},$_->{'severity'},$->{'mode'},$_->{'resource'},$_->{'sdmode'}\n";
510
my ($exportLog, $db, $header) = @_;
512
if (open(LOG, ">$exportLog")) {
514
my $date = localtime();
516
if ($exportLog =~ /csv/) {
518
# $header comes from reportgen.pl (scheduled reports)
519
if ($header) { print LOG "$header\n\n"; }
523
# host time prog profile pid severity resource sdmode mode
524
#print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},$_->{'pid'},";
525
print LOG "$_->{'host'},$_->{'date'},$_->{'prog'},$_->{'profile'},$_->{'pid'},";
526
print LOG "$_->{'severity'},$_->{'mode'},$_->{'resource'},$_->{'sdmode'}\n";
530
} elsif ($exportLog =~ /html/) {
532
print LOG "<html><body bgcolor='fffeec'>\n\n";
533
print LOG "<font face='Helvetica,Arial,Sans-Serif'>\n";
535
# $header comes from reportgen.pl (scheduled reports)
537
print LOG "$header\n\n";
539
print LOG "<br><h3>$exportLog</h3><br>\n<h4>Log generated by Novell AppArmor, $date</h4>\n\n";
542
print LOG "<hr><br><table border='1' cellpadding='2'>\n";
544
#print LOG "<tr bgcolor='edefff'><th>Date</th><th>Profile</th><th>PID</th><th>Message</th></tr>\n";
545
print LOG "<tr bgcolor='edefff'><th>Host</th><th>Date</th><th>Program</th><th>Profile</th><th>PID</th>"
546
. "<th>Severity</th><th>Mode</th><th>Detail</th><th>Access Type</th></tr>\n";
554
#"<td> $_->{'time'} </td>" .
555
print LOG "<tr><td> $_->{'host'} </td>"
556
. "<td> $_->{'date'} </td>"
557
. "<td> $_->{'prog'} </td>"
558
. "<td> $_->{'profile'} </td>"
559
. "<td> $_->{'pid'} </td>"
560
. "<td> $_->{'severity'} </td>"
561
. "<td> $_->{'mode'} </td>"
562
. "<td> $_->{'resource'} </td>"
563
. "<td> $_->{'sdmode'} </td></tr>\n";
567
# Shade every other row
568
print LOG "<tr='edefef'><td> $_->{'host'} </td>"
569
. "<td> $_->{'date'} </td>"
570
. "<td> $_->{'prog'} </td>"
571
. "<td> $_->{'profile'} </td>"
572
. "<td> $_->{'pid'} </td>"
573
. "<td> $_->{'severity'} </td>"
574
. "<td> $_->{'mode'} </td>"
575
. "<td> $_->{'resource'} </td>"
576
. "<td> $_->{'sdmode'} </td></tr>\n";
581
print LOG "<br></table></font></body></html>\n\n";
586
ycp::y2error(sprintf(gettext("Export Log Error: Couldn't open %s"), $exportLog));
592
# Pulls info on single report from apparmor xml file
594
my ($repName, $repConf) = @_;
599
if (defined($repName) && ref($repName)) {
601
if ($repName->{'base'}) {
602
$repName = $repName->{'base'};
603
} elsif ($repName->{'name'}) {
604
$repName = $repName->{'name'};
609
ycp::y2error(gettext("Fatal error. No report name given. Exiting."));
612
if (!$repConf || !-e $repConf) {
613
$repConf = '/etc/apparmor/reports.conf';
618
"Unable to get configuration info for %s.
629
if (open(XML, "<$repConf")) {
637
#my $name = (split(/\"/, $_))[1];
638
/\<name\>(.+)\<\/name\>/;
640
if ($name eq $repName) {
641
$rep{'name'} = $name;
645
} elsif (/\<\/report\>/) {
649
} elsif ($repFlag == 1) {
650
if (/\s*\<\w+\s+(.*)\/\>.*$/) {
653
my @attrlist = split(/\s+/, $attrs);
657
if (/\s*(\S+)=\"(\S+)\"/) {
658
$rep{$1} = $2 unless $2 eq '-';
661
} elsif (/\<(\w+)\>([\w+|\/].*)\<\//) {
664
$rep{"$1"} = $2 unless $2 eq '-';
666
ycp::y2error(sprintf(gettext("Failed to parse: %s."), $_));
675
ycp::y2error(sprintf(gettext("Fatal Error. Couldn't open %s."), $repConf));
682
# Returns info on currently confined processes
688
my $cfApp = '/usr/sbin/unconfined';
690
if (open(CF, "$cfApp |")) {
692
my $host = `hostname`;
695
my $date = localtime;
701
$ref->{'host'} = $host;
702
$ref->{'date'} = $date;
705
($ref->{'pid'}, $ref->{'prog'}, $all) = split(/\s+/, $_, 3);
706
$all = /\s*((not)*\s*confined\s*(by)*)/;
707
$ref->{'state'} = $1;
708
$ref->{'state'} =~ s/\s*by//g;
709
$ref->{'state'} =~ s/not\s+/not-/g;
710
($ref->{'prof'}, $ref->{'type'}) = split(/\s+/, $_);
712
if ($ref->{'prog'} eq "") { $ref->{'prog'} = "-"; }
713
if ($ref->{'prof'} eq "") { $ref->{'prof'} = "-"; }
714
if ($ref->{'pid'} eq "") { $ref->{'pid'} = "-"; }
715
if ($ref->{'state'} eq "") { $ref->{'state'} = "-"; }
716
if ($ref->{'type'} eq "") { $ref->{'type'} = "-"; }
723
my $error = sprintf(gettext("Fatal Error. Can't run %s. Exiting."), $cfApp);
724
ycp::y2error($error);
731
# generate stats for ESS reports
735
#my ($host, $targetDir, $startdate, $enddate) = @_;
739
my $targetDir = undef;
741
my $startdate = undef;
744
if (!$args->{'targetDir'}) {
745
$targetDir = '/var/log/apparmor/';
748
if ($args->{'host'}) { $host = $args->{'host'}; }
750
if ($args->{'startdate'}) {
751
$startdate = $args->{'startdate'};
753
$startdate = '1104566401'; # Jan 1, 2005
756
if ($args->{'enddate'}) {
757
$enddate = $args->{'enddate'};
762
if (!-e $targetDir) {
763
ycp::y2error(sprintf(gettext("Fatal Error. No directory, %s, found. Exiting."), $targetDir));
767
# Max Sev, Ave. Sev, Num. Rejects, Start Time, End Time
768
my $ctQuery = "SELECT count(*) FROM events WHERE time >= $startdate AND time <= $enddate";
770
my $query = "SELECT MAX(severity), AVG(severity), COUNT(id), MIN(time), "
771
. "MAX(time) FROM events WHERE sdmode='REJECTING' AND "
772
. "time >= $startdate AND time <= $enddate";
774
# "MAX(time) FROM events join info WHERE sdmode='REJECTING' AND " .
776
# Get list of hosts to scan
777
if (opendir(TDIR, $targetDir)) {
779
@hostList = grep(/\.db/, readdir(TDIR));
783
ycp::y2error(sprintf(gettext("Fatal Error. Couldn't open %s. Exiting"), $targetDir));
787
# Cycle through for each host
788
for my $eventDb (@hostList) {
790
$eventDb = "$targetDir/$eventDb";
796
#my $eventDb = '/var/log/apparmor/events.db';
798
my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 });
802
my $hostQuery = "SELECT * FROM info WHERE name='host'";
805
my $sth = $dbh->prepare($hostQuery);
807
$host = $sth->fetchrow_array();
812
ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr));
816
# Get number of events
818
my $sth = $dbh->prepare($ctQuery);
820
$count = $sth->fetchrow_array();
825
ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr));
830
eval { $ret = $dbh->selectall_arrayref("$query"); };
833
ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr));
839
# hostIp, startDate, endDate, sevHi, sevMean, numRejects
841
$ess->{'host'} = $host;
846
$ess->{'sevHi'} = $$ret[0]->[0];
848
if (!$ess->{'sevHi'}) {
852
$ess->{'sevMean'} = $$ret[0]->[1];
854
if (!$ess->{'sevMean'} || $ess->{'sevHi'} == 0) {
855
$ess->{'sevMean'} = 0;
857
$ess->{'sevMean'} = round("$ess->{'sevMean'}");
860
$ess->{'numRejects'} = $$ret[0]->[2];
861
$ess->{'startdate'} = $$ret[0]->[3];
862
$ess->{'enddate'} = $$ret[0]->[4];
863
$ess->{'numEvents'} = $count;
866
if ($ess->{'startdate'} && $ess->{'startdate'} !~ /:/) {
867
$ess->{'startdate'} = Immunix::Reports::getDate($ess->{'startdate'});
869
if ($ess->{'enddate'} && $ess->{'enddate'} !~ /:/) {
870
$ess->{'enddate'} = Immunix::Reports::getDate($ess->{'enddate'});
879
# get ESS stats for archived reports (warning -- this can be slow for large files
880
# debug -- not fully functional yet
881
sub getArchEssStats {
886
my $startDate = '1104566401'; # Jan 1, 2005
889
if ($args->{'startdate'}) { $startDate = $args->{'startdate'}; }
890
if ($args->{'enddate'}) { $endDate = $args->{'enddate'}; }
892
# hostIp, startDate, endDate, sevHi, sevMean, numRejects
893
my @eventDb = getEvents("$startDate", "$endDate");
895
my @hostIdx = (); # Simple index to all hosts for quick host matching
896
my @hostDb = (); # Host-keyed Data for doing REJECT stats
898
# Outer Loop for Raw Event db
903
my $ev = $_; # current event record
905
# Create new host entry, or add to existing
906
if (grep(/$ev->{'host'}/, @hostIdx) == 1) {
908
# Inner loop, but the number of hosts should be small
911
if ($_->{'host'} eq $ev->{'host'}) {
913
# Find earliest start date
914
if ($_->{'startdate'} > $ev->{'date'}) {
915
$_->{'startdate'} = $ev->{'date'};
918
# tally all events reported for host
921
if ($ev->{'sdmode'}) {
922
if ($ev->{'sdmode'} =~ /PERMIT/) {
923
$_->{'numPermits'}++;
925
if ($ev->{'sdmode'} =~ /REJECT/) {
926
$_->{'numRejects'}++;
928
if ($ev->{'sdmode'} =~ /AUDIT/) {
933
# Add stats to host entry
934
#if ( $ev->{'severity'} && $ev->{'severity'} =~ /\b\d+\b/ ) {}
935
if ($ev->{'severity'} && $ev->{'severity'} != -1) {
938
$_->{'sevTotal'} = $_->{'sevTotal'} + $ev->{'severity'};
940
if ($ev->{'severity'} > $_->{'sevHi'}) {
941
$_->{'sevHi'} = $ev->{'severity'};
953
push(@hostIdx, $ev->{'host'}); # Add host entry to index
955
$rec->{'host'} = $ev->{'host'};
956
$rec->{'startdate'} = $startDate;
958
#$rec->{'startdate'} = $ev->{'date'};
961
$rec->{'enddate'} = $endDate;
963
$rec->{'enddate'} = time;
966
# Add stats to host entry
967
if ($ev->{'sev'} && $ev->{'sev'} ne "U") {
969
$rec->{'sevHi'} = $ev->{'sev'};
970
$rec->{'sevTotal'} = $ev->{'sev'};
971
$rec->{'sevNum'} = 1;
972
$rec->{'unknown'} = 0;
977
$rec->{'sevTotal'} = 0;
978
$rec->{'sevNum'} = 0;
979
$rec->{'unknown'} = 1;
984
$rec->{'numPermits'} = 0;
985
$rec->{'numRejects'} = 0;
986
$rec->{'numAudits'} = 0;
987
$rec->{'numEvents'} = 1; # tally all events reported for host
989
if ($ev->{'sdmode'}) {
990
if ($ev->{'sdmode'} =~ /PERMIT/) { $rec->{'numPermits'}++; }
991
if ($ev->{'sdmode'} =~ /REJECT/) { $rec->{'numRejects'}++; }
992
if ($ev->{'sdmode'} =~ /AUDIT/) { $rec->{'numAudits'}++; }
995
push(@hostDb, $rec); # Add new records to host data list
999
next; # Missing host info -- big problem
1001
} # END @eventDb loop
1003
# Process simple REJECT-related stats (for Executive Security Summaries)
1006
# In the end, we want this info:
1007
# - Hostname, Startdate, Enddate, # Events, # Rejects, Ave. Severity, High Severity
1009
if ($_->{'sevTotal'} > 0 && $_->{'sevNum'} > 0) {
1010
$_->{'sevMean'} = round($_->{'sevTotal'} / $_->{'sevNum'});
1012
$_->{'sevMean'} = 0;
1016
if ($_->{'startdate'} !~ /:/) {
1017
$_->{'startdate'} = getDate($startDate);
1019
if ($_->{'enddate'} !~ /:/) {
1020
$_->{'enddate'} = getDate($_->{'enddate'});
1023
# Delete stuff that we may use in later versions (YaST is a silly,
1024
# silly data handler)
1025
delete($_->{'sevTotal'});
1026
delete($_->{'sevNum'});
1027
delete($_->{'numPermits'});
1028
delete($_->{'numAudits'});
1029
delete($_->{'unknown'});
1036
# special version of getEvents() for /usr/bin/reportgen.pl
1038
my ($rep, $start, $end) = @_;
1044
my $query = "SELECT * FROM events ";
1046
# Clear unnecessary filters
1047
if ($rep->{'prog'}) { $rep->{'prog'} =~ s/\s+//g; }
1048
if ($rep->{'prof'}) { $rep->{'prof'} =~ s/\s+//g; }
1049
if ($rep->{'mode'}) { $rep->{'mode'} =~ s/\s+//g; }
1050
if ($rep->{'sdmode'}) { $rep->{'sdmode'} =~ s/\s+//g; }
1051
if ($rep->{'sev'}) { $rep->{'sev'} =~ s/\s+//g; }
1052
if ($rep->{'res'}) { $rep->{'res'} =~ s/\s+//g; }
1054
if ($rep->{'prog'} && ($rep->{'prog'} eq "-" || $rep->{'prog'} eq "All")) {
1055
delete($rep->{'prog'});
1057
if ($rep->{'prof'} && $rep->{'prof'} eq "-") { delete($rep->{'prof'}); }
1058
if ($rep->{'pid'} && $rep->{'pid'} eq "-") { delete($rep->{'pid'}); }
1059
if ($rep->{'sev'} && ($rep->{'sev'} eq "-" || $rep->{'sev'} eq "All")) {
1060
delete($rep->{'sev'});
1062
if ($rep->{'resource'} && $rep->{'resource'} eq "-") {
1063
delete($rep->{'resource'});
1066
if ($rep->{'mode'} && ($rep->{'mode'} eq "-" || $rep->{'mode'} eq "All")) {
1067
delete($rep->{'mode'});
1070
if ($rep->{'sdmode'}
1071
&& ($rep->{'sdmode'} eq "-" || $rep->{'sdmode'} eq "All"))
1073
delete($rep->{'sdmode'});
1076
$rep = rewriteModes($rep);
1078
# Set Dates far enough apart to get all entries (ie. no date filter)
1079
my $startDate = '1104566401'; # Jan 1, 2005
1082
if ($start && $start > 0) { $startDate = $start; }
1085
my $midQuery = getQueryFilters($rep, $startDate, $endDate);
1086
$query .= "$midQuery";
1089
$db = getEvents($query, "$startDate", "$endDate");
1094
sub getQueryFilters {
1095
my ($filts, $start, $end) = @_;
1102
# Match any requested filters or drop record
1103
############################################################
1104
if ($filts->{'prog'}) {
1105
$query .= "WHERE events.prog = \'$filts->{'prog'}\' ";
1109
if ($filts->{'profile'} && $_->{'profile'}) {
1111
$query .= "AND events.profile = \'$filts->{'profile'}\' ";
1113
$query .= "WHERE events.profile = \'$filts->{'profile'}\' ";
1118
if ($filts->{'pid'}) {
1120
$query .= "AND events.pid = \'$filts->{'pid'}\' ";
1122
$query .= "WHERE events.pid = \'$filts->{'pid'}\' ";
1127
if ($filts->{'severity'}) {
1128
if ($filts->{'severity'} eq "-" || $filts->{'severity'} eq "All") {
1129
delete($filts->{'severity'});
1130
} elsif ($filts->{'severity'} eq "-1"
1131
|| $filts->{'severity'} eq "U")
1134
$query .= "AND events.severity = '-1' ";
1136
$query .= "WHERE events.severity = '-1' ";
1141
$query .= "AND events.severity >= \'$filts->{'severity'}\' ";
1143
$query .= "WHERE events.severity >= \'$filts->{'severity'}\' ";
1149
if ($filts->{'resource'}) {
1151
$query .= "AND events.resource LIKE '%$filts->{'resource'}%' ";
1153
$query .= "WHERE events.resource LIKE '%$filts->{'resource'}%' ";
1158
if ($filts->{'mode'}) {
1160
$query .= "AND events.mode LIKE '%$filts->{'mode'}%' ";
1162
$query .= "WHERE events.mode LIKE '%$filts->{'mode'}%' ";
1167
if ($filts->{'sdmode'}) {
1169
if ($filts->{'sdmode'} =~ /\|/) {
1171
my @sdmunge = split(/\|/, $filts->{'sdmode'});
1172
for (@sdmunge) { $_ = "\'\%" . "$_" . "\%\'"; }
1174
$filts->{'sdmode'} = join(" OR events.sdmode LIKE ", @sdmunge);
1177
$filts->{'sdmode'} = "\'\%" . "$filts->{'sdmode'}" . "\%\'";
1181
$query .= "AND events.sdmode LIKE $filts->{'sdmode'} ";
1183
$query .= "WHERE events.sdmode LIKE $filts->{'sdmode'} ";
1190
if ($start && $start =~ /\d+/ && $start > 0) {
1192
$query .= "AND events.time >= $start ";
1194
$query .= "WHERE events.time >= $start ";
1199
if ($end && $end =~ /\d+/ && $end > $start) {
1201
$query .= "AND events.time <= $end ";
1203
$query .= "WHERE events.time <= $end ";
1211
my ($filts, $page, $sortKey, $numEvents) = @_;
1213
if (!$page || $page < 1 || $page !~ /\d+/) { $page = 1; }
1214
if (!$sortKey) { $sortKey = 'time'; }
1215
if (!$numEvents) { $numEvents = '1000'; }
1217
my $limit = (($page * $numEvents) - $numEvents);
1219
my $query = "SELECT * FROM events ";
1222
my $midQuery = getQueryFilters($filts);
1223
$query .= "$midQuery";
1227
$query .= "Order by $sortKey LIMIT $limit,$numEvents";
1232
# - This should exec AFTER the initial select (should limit the number of records
1233
# that we'll be mangling
1234
# - There may be a way to do this with a creative query statement generator
1236
sub queryPostProcess {
1245
# Shuffle special events into appropriate column variables
1246
############################################################
1247
if ($_->{'attrch'}) { $_->{'sdmode'} .= " $_->{'attrch'}"; }
1251
if ($_->{'type'} eq 'control_variable') {
1253
# OWLSM gets special treatment
1254
if ($_->{'variable'} eq 'owlsm') {
1256
#if ( $_->{'value'} ) {}
1257
if ($_->{'value'} == '0') {
1258
$_->{'resource'} = "GLOBAL MODULE CHANGE: OWLSM DISABLED";
1259
} elsif ($_->{'value'} == '1') {
1260
$_->{'resource'} = "GLOBAL MODULE CHANGE: OWLSM ENABLED";
1262
$_->{'resource'} = "Unrecognized OWLSM activity.";
1265
$_->{'resource'} = "$_->{'variable'}";
1267
} elsif ($_->{'type'} eq 'capability') {
1268
$_->{'resource'} .= " $_->{'capability'}";
1270
} elsif ($_->{'type'} eq 'attribute_change') {
1271
$_->{'sdmode'} .= " $_->{'attribute'} change";
1272
} elsif ($_->{'type'} eq 'subdomain_insmod') {
1273
$_->{'resource'} = "AppArmor Started";
1274
} elsif ($_->{'type'} eq 'subdomain_rmmod') {
1275
$_->{'resource'} = "AppArmor Stopped";
1277
# DROP logprof-hints
1278
} elsif ($_->{'type'} eq 'unknown_hat') {
1281
# DROP logprof-hints
1282
} elsif ($_->{'type'} eq 'changing_profile') {
1285
# DROP logprof-hints
1286
} elsif ($_->{'type'} eq 'fork') {
1288
} elsif ($_->{'type'} ne 'path') {
1289
$_->{'resource'} .= " $_->{'type'}";
1293
# Convert Epoch Time to Date
1294
if ($_->{'time'} && $_->{'time'} == $prevTime) {
1295
$_->{'date'} = $prevDate;
1296
} elsif ($_->{'time'}) {
1297
my $newDate = getDate("$_->{'time'}");
1298
$_->{'date'} = $newDate;
1299
$prevDate = $newDate;
1300
$prevTime = $_->{'time'};
1302
$_->{'date'} = "0000-00-00 00:00:00";
1305
# $_->{'time'} = undef; # Don't need 'time', only 'date'
1306
if (!$_->{'host'}) { $_->{'host'} = "-"; }
1307
if (!$_->{'date'}) { $_->{'date'} = "-"; }
1308
if (!$_->{'prog'}) { $_->{'prog'} = "-"; }
1309
if (!$_->{'profile'}) { $_->{'profile'} = "-"; }
1310
if (!$_->{'pid'}) { $_->{'pid'} = "-"; }
1311
if (!$_->{'mode'}) { $_->{'mode'} = "-"; }
1312
if (!$_->{'resource'}) { $_->{'resource'} = "-"; }
1313
if (!$_->{'sdmode'}) { $_->{'sdmode'} = "-"; }
1315
if (!$_->{'severity'}) {
1316
$_->{'severity'} = "-";
1317
} elsif ($_->{'severity'} eq "-1") {
1318
$_->{'severity'} = "U";
1320
# $_->{'severity'} = sprintf("%02d", $_->{'severity'});
1323
push(@newDb, $_); # Don't quote the $_ (breaks hash)
1330
# Creates single hashref for the various filters
1331
sub setFormFilters {
1338
if ($args->{'prog'}) { $filts->{'prog'} = $args->{'prog'}; }
1339
if ($args->{'profile'}) { $filts->{'profile'} = $args->{'profile'}; }
1340
if ($args->{'pid'}) { $filts->{'pid'} = $args->{'pid'}; }
1341
if ($args->{'resource'}) { $filts->{'resource'} = $args->{'resource'}; }
1342
if ($args->{'severity'}) { $filts->{'severity'} = $args->{'severity'}; }
1343
if ($args->{'sdmode'}) { $filts->{'sdmode'} = $args->{'sdmode'}; }
1344
if ($args->{'mode'}) { $filts->{'mode'} = $args->{'mode'}; }
1351
# helper for getSirFilters()
1352
# Makes gui-centric filters querying-friendly
1353
sub rewriteFilters {
1356
# Clear unnecessary filters
1357
for (keys(%$filts)) {
1358
if ($filts->{$_} eq "All") { delete($filts->{$_}); }
1361
if ($filts->{'prog'}
1362
&& ($filts->{'prog'} eq "-" || $filts->{'prog'} eq "All"))
1364
delete($filts->{'prog'});
1366
if ($filts->{'profile'} && ($filts->{'profile'} eq "-")) {
1367
delete($filts->{'profile'});
1369
if ($filts->{'pid'} && ($filts->{'pid'} eq "-")) {
1370
delete($filts->{'pid'});
1372
if ($filts->{'severity'} && ($filts->{'severity'} eq "-")) {
1373
delete($filts->{'severity'});
1375
if ($filts->{'resource'} && ($filts->{'resource'} eq "-")) {
1376
delete($filts->{'resource'});
1379
if ($filts->{'mode'}
1380
&& ($filts->{'mode'} eq "-" || $filts->{'mode'} eq "All"))
1382
delete($filts->{'mode'});
1385
if ($filts->{'sdmode'}
1386
&& ($filts->{'sdmode'} eq "-" || $filts->{'sdmode'} eq "All"))
1388
delete($filts->{'sdmode'});
1390
############################################################
1392
$filts = rewriteModes($filts);
1397
# returns ref to active filters for the specific SIR report
1401
my $repName = undef;
1403
if ($args && $args->{'name'}) {
1404
$repName = $args->{'name'};
1406
$repName = "Security.Incident.Report";
1409
my $repConf = '/etc/apparmor/reports.conf';
1412
my $filts = getXmlReport($repName);
1414
# Clean hash of useless refs
1415
for (sort keys(%$filts)) {
1416
if ($filts->{$_} eq "-") {
1417
delete($filts->{$_});
1421
# remove non-filter info
1422
if ($filts->{'name'}) { delete($filts->{'name'}); }
1423
if ($filts->{'exportpath'}) { delete($filts->{'exportpath'}); }
1424
if ($filts->{'exporttype'}) { delete($filts->{'exporttype'}); }
1425
if ($filts->{'addr1'}) { delete($filts->{'addr1'}); }
1426
if ($filts->{'addr2'}) { delete($filts->{'addr2'}); }
1427
if ($filts->{'addr3'}) { delete($filts->{'addr3'}); }
1428
if ($filts->{'time'}) { delete($filts->{'time'}); }
1430
if (!$args->{'gui'} || $args->{'gui'} ne "1") {
1431
$filts = rewriteModes($filts);
1432
$filts = rewriteFilters($filts);
1438
# deprecated (pre-xml)
1439
sub OldgetSirFilters {
1442
my $repName = undef;
1444
if ($args && $args->{'name'}) {
1445
$repName = $args->{'name'};
1448
my $repConf = '/etc/apparmor/reports.conf';
1452
$repName = "\"Security.Incident.Report\"";
1454
$repName = "\"$repName\"";
1457
if (open(CF, "<$repConf")) {
1462
my ($cfRptName) = (split(/:/, $_))[0];
1463
$cfRptName =~ s/\s+$//; # remove trailing spaces
1465
next unless ($cfRptName eq "$repName");
1467
# Name : csv.html : prog, prof, pid, res, sev, sdmode, mode : (up to 3) email addresses : last run time
1468
my ($name, $info) = split(/:/, $_, 2);
1472
my ($mailtype, $filters, $email, $lastRun) =
1473
split(/\s*:\s*/, $info, 4);
1475
$rec->{'name'} = $name;
1476
$rec->{'name'} =~ s/\"//g;
1477
($rec->{'prog'}, $rec->{'profile'}, $rec->{'pid'}, $rec->{'resource'}, $rec->{'severity'}, $rec->{'sdmode'}, $rec->{'mode'}) =
1478
split(/\,/, $filters, 7);
1484
logError("Couldn't open $repConf. No filters will be used in report generation.");
1488
# Clean hash of useless refs
1489
for (sort keys(%$rec)) {
1490
if ($rec->{$_} eq "-") {
1495
$rec = rewriteModes($rec);
1497
if (!$args->{'gui'} || $args->{'gui'} ne "1") {
1498
$rec = rewriteFilters($rec);
1504
# Main SIR report generator
1506
my ($query, $start, $end, $dbFile) = @_;
1512
if (!$query || $query !~ /^SELECT/) { $query = "SELECT * FROM events"; }
1513
if ($dbFile && -f $dbFile) { $eventDb = $dbFile; }
1515
my $hostName = `/bin/hostname` || 'unknown';
1516
chomp $hostName unless $hostName eq 'unknown';
1518
if (!$start) { $start = '1104566401'; } # Give default start of 1/1/2005
1519
if (!$end) { $end = time; }
1521
# make sure they don't give us a bad range
1522
($start, $end) = ($end, $start) if $start > $end;
1525
# - (id,time,counter,pid,sdmode,type,mode,resource,target,profile,prog,severity);
1527
# Pull stuff from db
1528
my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 });
1530
eval { $all = $dbh->selectall_arrayref("$query"); };
1533
ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
1539
for my $row (@$all) {
1541
($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;
1543
# Give empty record values a default value
1544
if (!$rec->{'host'}) { $rec->{'host'} = $hostName; }
1546
if (!$rec->{$_}) { $rec->{$_} = '-'; }
1549
# Change 'time' to date
1550
if ($rec->{'time'} && $rec->{'time'} == $prevTime) {
1551
$rec->{'date'} = $prevDate;
1552
} elsif ($rec->{'time'}) {
1553
my $newDate = getDate("$rec->{'time'}");
1554
$rec->{'date'} = $newDate;
1555
$prevDate = $newDate;
1556
$prevTime = $rec->{'time'};
1558
$rec->{'date'} = "0000-00-00-00:00:00";
1561
if ($rec->{'severity'} && $rec->{'severity'} eq '-1') {
1562
$rec->{'severity'} = 'U';
1565
delete($rec->{'time'});
1566
delete($rec->{'counter'});
1568
push(@events, $rec);
1574
# Archived Reports Stuff -- Some of this would go away in an ideal world
1575
################################################################################
1580
my $eventRep = "/var/log/apparmor/reports/events.rpt";
1582
#if ( $args->{'type'} && $args->{'type'} eq 'archRep' ) {}
1583
if ($args->{'logFile'}) {
1584
$eventRep = $args->{'logFile'};
1587
if (open(REP, "<$eventRep")) {
1591
if ($args->{'page'}) { $page = $args->{'page'}; }
1598
my $prevTime = undef;
1599
my $prevDate = undef;
1605
# Why not get rid of page and just do divide by $i later?
1610
if ($_ eq "Page $page") {
1615
} elsif ($slurp == 1) {
1619
($db->{'host'}, $db->{'time'}, $db->{'prog'}, $db->{'profile'}, $db->{'pid'}, $db->{'severity'}, $db->{'mode'}, $db->{'denyRes'}, $db->{'sdmode'}) = split(/\,/, $_);
1621
# Convert epoch time to date
1622
if ($db->{'time'} == $prevTime) {
1623
$db->{'date'} = $prevDate;
1625
$prevTime = $db->{'time'};
1626
$prevDate = getDate("$db->{'time'}");
1627
$db->{'date'} = $prevDate;
1631
$db->{'date'} = $db->{'time'};
1632
delete $db->{'time'};
1640
ycp::y2error(sprintf(gettext("Fatal Error. getArchReport() couldn't open %s"), $eventRep));
1641
return ("Couldn't open $eventRep");
1647
sub writeEventReport {
1648
my ($db, $args) = @_; # Filters for date, && regexp
1649
# my $type = shift || undef;
1651
my $eventRep = "/var/log/apparmor/reports/events.rpt";
1653
# Not sure if this is needed anymore, but it messes up archived SIR reports
1654
# if ( $args->{'logFile'} ) { $eventRep = $args->{'logFile'}; }
1656
if (open(REP, ">$eventRep")) {
1662
# Title for scheduled reports
1663
if ($args->{'title'}) { print REP "$args->{'title'}"; }
1665
print REP "Page $page\n";
1670
print REP "$_->{'host'},$_->{'date'},$_->{'prog'},$_->{'profile'},$_->{'pid'},$_->{'severity'},$_->{'mode'},$_->{'resource'},$_->{'sdmode'}\n";
1672
if (($i % $numEvents) == 0 && $skip == 0) {
1673
print REP "Page $page\n";
1686
return ("Couldn't open $eventRep");
1695
my $dir = '/var/log/apparmor/reports-archived';
1697
my @errors = (); # For non-fatal errors
1700
my $eventRep = "/var/log/apparmor/reports/all-reports.rpt"; # write summary to this file - changed 04-14-2005
1701
#my $eventRep = "/tmp/events.rpt"; # write summary to this file
1703
if ($args->{'logFile'}) { $readFile = $args->{'logFile'}; }
1704
if ($args->{'repPath'}) { $dir = $args->{'repPath'}; }
1711
# Open record compilation file
1712
if (open(RREP, "<$dir/$readFile")) {
1714
if (open(WREP, ">$eventRep")) {
1716
# print WREP "Page $numPages\n";
1726
if (($numRecords % $numEvents) == 0 && $skip == 0) {
1727
print WREP "Page $numPages\n";
1738
$error = "Problem in prepSingleLog() - couldn't open $eventRep.";
1745
$error = "Problem in prepSingleLog() - couldn't open -$dir/$readFile-.";
1752
# Cats files in specified directory for easier parsing
1753
sub prepArchivedLogs {
1756
my $dir = '/var/log/apparmor/reports-archived';
1758
my @errors = (); # For non-fatal errors
1761
my $eventRep = "/var/log/apparmor/reports/all-reports.rpt";
1765
if ($args->{'logFile'}) {
1766
$eventRep = $args->{'logFile'};
1769
if ($args->{'repPath'}) {
1770
$dir = $args->{'repPath'};
1773
# Check to see if we need to use filters
1775
&& ($args->{'mode'} =~ /All/ || $args->{'mode'} =~ /^\s*-\s*$/))
1777
delete($args->{'mode'});
1779
if ($args->{'sdmode'}
1780
&& ($args->{'sdmode'} =~ /All/ || $args->{'sdmode'} =~ /^\s*-\s*$/))
1782
delete($args->{'sdmode'});
1784
if ($args->{'resource'}
1785
&& ($args->{'resource'} =~ /All/ || $args->{'resource'} =~ /^\s*-\s*$/))
1787
delete($args->{'resource'});
1789
if ($args->{'sevLevel'}
1790
&& ($args->{'sevLevel'} =~ /All/ || $args->{'sevLevel'} =~ /^\s*-\s*$/))
1792
delete($args->{'sevLevel'});
1795
if ( $args->{'prog'}
1796
|| $args->{'profile'}
1798
|| $args->{'denyRes'}
1800
|| $args->{'sdmode'}
1801
|| ($args->{'startdate'} && $args->{'enddate'}))
1806
############################################################
1808
# Get list of files in archived report directory
1809
if (opendir(RDIR, $dir)) {
1811
my @firstPass = grep(/csv/, readdir(RDIR));
1813
grep(!/Applications.Audit|Executive.Security.Summary/, @firstPass);
1817
$error = "Failure in prepArchivedLogs() - couldn't open $dir.";
1818
return ($error); # debug - exit instead?
1825
# Open record compilation file
1826
if (open(AREP, ">$eventRep")) {
1832
# Cycle through each $file in $dir
1833
if (open(RPT, "<$dir/$file")) {
1834
push(@rawDb, <RPT>);
1837
$error = "Problem in prepArchivedLogs() - couldn't open $dir/$file.";
1838
push(@errors, $error);
1842
# sort & store cat'd files
1846
if ($useFilters == 1) {
1848
my @tmpDb = parseMultiDb($args, @rawDb);
1856
print AREP "Page $numPages\n";
1866
if (($numRecords % $numEvents) == 0 && $skip == 0) {
1867
print AREP "Page $numPages\n";
1877
$error = "DB created from $dir is empty.";
1883
$error = "Problem in prepArchivedLogs() - couldn't open $eventRep.";
1884
push(@errors, $error);
1890
# Similar to parseLog(), but expects @db to be passed
1892
my ($args, @db) = @_;
1897
my $startDate = undef;
1898
my $endDate = undef;
1900
# deref dates for speed
1901
if ($args->{'startdate'} && $args->{'enddate'}) {
1902
$startDate = getEpochFromNum("$args->{'startdate'}", 'start');
1903
$endDate = getEpochFromNum("$args->{'enddate'}", 'end');
1906
$args = rewriteModes($args);
1913
next if /true|false/; # avoid horrible yast bug
1917
next if (!$_ || $_ eq "");
1919
# Lazy filters -- maybe these should be with the rest below
1920
if ($args->{'prog'}) { next unless /$args->{'prog'}/; }
1921
if ($args->{'profile'}) { next unless /$args->{'profile'}/; }
1923
# Need (epoch) 'time' element here, do we want to store 'date' instead?
1924
($rec->{'host'}, $rec->{'time'}, $rec->{'prog'}, $rec->{'profile'}, $rec->{'pid'}, $rec->{'sevLevel'}, $rec->{'mode'}, $rec->{'resource'}, $rec->{'sdmode'}) = split(/\,/, $_);
1926
# Make sure we get the time/date ref. name right. If it's $args->"time",
1927
# the arg will be converted to a human-friendly "date" ref in writeEventReport().
1928
if ($rec->{'time'} =~ /\:|\-/) {
1929
$rec->{'date'} = $rec->{'time'};
1930
delete $rec->{'time'};
1934
if ($args->{'pid'} && $args->{'pid'} ne '-') {
1935
next unless ($args->{'pid'} eq $rec->{'pid'});
1937
if ( $args->{'sevLevel'}
1938
&& $args->{'sevLevel'} ne "00"
1939
&& $args->{'sevLevel'} ne '-')
1941
if ($args->{'sevLevel'} eq "U") { $args->{'sevLevel'} = '-1'; }
1942
next unless ($args->{'sevLevel'} eq $rec->{'sevLevel'});
1944
if ($args->{'mode'} && $args->{'mode'} ne '-') {
1945
next unless ($args->{'mode'} eq $rec->{'mode'});
1948
if ($args->{'denyRes'} && $args->{'denyRes'} ne '-') {
1949
next unless ($args->{'denyRes'} eq $rec->{'denyRes'});
1951
if ($args->{'sdmode'} && $args->{'sdmode'} ne '-') {
1953
# Needs reversal of comparison for sdmode
1954
next unless ($rec->{'sdmode'} =~ /$args->{'sdmode'}/);
1957
push(@newDb, $line);
1964
# Grab & filter events from archived reports (.csv files)
1969
my $eventRep = "/var/log/apparmor/reports/events.rpt";
1971
if ($args->{'logFile'}) {
1972
$eventRep = $args->{'logFile'};
1975
#my $id = keys(%$db);
1978
my $startDate = undef;
1979
my $endDate = undef;
1981
# deref dates for speed
1982
if ($args->{'startdate'} && $args->{'enddate'}) {
1983
$startDate = getEpochFromNum("$args->{'startdate'}", 'start');
1984
$endDate = getEpochFromNum("$args->{'enddate'}", 'end');
1987
#if ( $args->{'mode'} && ( $args->{'mode'} =~ /All/ || $args->{'mode'} =~ /\s*\-\s*/) ) {}
1989
&& ($args->{'mode'} =~ /All/ || $args->{'mode'} =~ /^\s*-\s*$/))
1991
delete($args->{'mode'});
1993
if ($args->{'sdmode'}
1994
&& ($args->{'sdmode'} =~ /All/ || $args->{'sdmode'} =~ /^\s*-\s*$/))
1996
delete($args->{'sdmode'});
1998
if ($args->{'resource'}
1999
&& ($args->{'resource'} =~ /All/ || $args->{'resource'} =~ /^\s*-\s*$/))
2001
delete($args->{'resource'});
2003
if ($args->{'sevLevel'}
2004
&& ($args->{'sevLevel'} =~ /All/ || $args->{'sevLevel'} =~ /^\s*-\s*$/))
2006
delete($args->{'sevLevel'});
2009
$args = rewriteModes($args);
2011
if (open(LOG, "<$eventRep")) {
2018
next if /true|false/; # avoid horrible yast bug
2022
next if (!$_ || $_ eq "");
2024
# Lazy filters -- maybe these should be with the rest below
2025
if ($args->{'prog'}) { next unless /$args->{'prog'}/; }
2026
if ($args->{'profile'}) { next unless /$args->{'profile'}/; }
2028
# Need (epoch) 'time' element here, do we want to store 'date' instead?
2029
($rec->{'host'}, $rec->{'time'}, $rec->{'prog'}, $rec->{'profile'}, $rec->{'pid'}, $rec->{'sevLevel'}, $rec->{'mode'}, $rec->{'resource'}, $rec->{'sdmode'}) = split(/\,/, $_);
2031
# Make sure we get the time/date ref. name right. If it's $args->"time",
2032
# the arg will be converted to a human-friendly "date" ref in writeEventReport().
2033
if ($rec->{'time'} =~ /\:|\-/) {
2034
$rec->{'date'} = $rec->{'time'};
2035
delete $rec->{'time'};
2039
if ($args->{'pid'} && $args->{'pid'} ne '-') {
2040
next unless ($args->{'pid'} eq $rec->{'pid'});
2042
if ( $args->{'sevLevel'}
2043
&& $args->{'sevLevel'} ne "00"
2044
&& $args->{'sevLevel'} ne '-')
2046
next unless ($args->{'sevLevel'} eq $rec->{'sevLevel'});
2048
if ($args->{'mode'} && $args->{'mode'} ne '-') {
2049
next unless ($args->{'mode'} eq $rec->{'mode'});
2051
if ($args->{'denyRes'} && $args->{'denyRes'} ne '-') {
2052
next unless ($args->{'denyRes'} eq $rec->{'denyRes'});
2054
if ($args->{'sdmode'} && $args->{'sdmode'} ne '-') {
2056
# Needs reversal of comparison for sdmode
2057
next unless ($rec->{'sdmode'} =~ /$args->{'sdmode'}/);
2066
# Export results to file if requested
2067
if ($args->{'exporttext'} || $args->{'exporthtml'}) {
2072
if ($args->{'exportPath'}) {
2073
$rawLog = $args->{'exportPath'} . '/export-log';
2075
$rawLog = '/var/log/apparmor/reports-exported/export-log';
2078
if ($args->{'exporttext'} && $args->{'exporttext'} eq 'true') {
2079
$expLog = "$rawLog.csv";
2080
exportLog($expLog, \@db); # redo w/ @$db instead of %db?
2083
if ($args->{'exporthtml'} && $args->{'exporthtml'} eq 'true') {
2084
$expLog = "$rawLog.html";
2085
exportLog($expLog, \@db); # redo w/ @$db instead of %db?
2089
# write out files to single sorted file (for state, and to speed up yast)
2090
#if (! $args->{'single'} ) {
2091
# $error = writeEventReport(\@db, $args);
2094
# changed 04-13-05 - should probably do this, regardless
2095
$error = writeEventReport(\@db, $args);
2098
$error = "Couldn't open $eventRep.";
2104
# OLD STUFF -- delete
2106
# deprecated -- replaced by better SQL queries
2107
sub OLDgetEssStats {
2112
my $startDate = '1104566401'; # Jan 1, 2005
2115
if ($args->{'startdate'}) { $startDate = $args->{'startdate'}; }
2116
if ($args->{'enddate'}) { $endDate = $args->{'enddate'}; }
2118
my $query = "SELECT * FROM events";
2120
# hostIp, startDate, endDate, sevHi, sevMean, numRejects
2121
my $eventDb = getEvents($query, "", "$startDate", "$endDate");
2123
my @hostIdx = (); # Simple index to all hosts for quick host matching
2124
my @hostDb = (); # Host-keyed data for doing REJECT stats
2126
# Outer Loop for Raw Event db
2129
my $ev = $_; # current event record
2131
if ($ev->{'host'}) {
2133
# Create new host entry, or add to existing
2134
if (grep(/$ev->{'host'}/, @hostIdx) == 1) {
2136
# Inner loop, but the number of hosts should be small
2137
for my $hdb (@hostDb) {
2139
if ($hdb->{'host'} eq $ev->{'host'}) {
2141
if ($hdb->{'startdate'} gt $ev->{'date'}) {
2142
$hdb->{'startdate'} = $ev->{'date'}; # Find earliest start date
2145
$hdb->{'numEvents'}++; # tally all events reported for host
2147
if ($ev->{'sdmode'}) {
2148
if ($ev->{'sdmode'} =~ /PERMIT/) {
2149
$hdb->{'numPermits'}++;
2151
if ($ev->{'sdmode'} =~ /REJECT/) {
2152
$hdb->{'numRejects'}++;
2154
if ($ev->{'sdmode'} =~ /AUDIT/) {
2155
$hdb->{'numAudits'}++;
2159
# Add stats to host entry
2160
#if ( $ev->{'severity'} && $ev->{'severity'} =~ /\b\d+\b/ ) {}
2161
if ($ev->{'severity'} && $ev->{'severity'} != -1) {
2164
$hdb->{'sevTotal'} = $hdb->{'sevTotal'} + $ev->{'severity'};
2166
if ($ev->{'severity'} > $hdb->{'sevHi'}) {
2167
$hdb->{'sevHi'} = $ev->{'severity'};
2170
$hdb->{'unknown'}++;
2179
push(@hostIdx, $ev->{'host'}); # Add host entry to index
2181
$rec->{'host'} = $ev->{'host'};
2182
$rec->{'startdate'} = $startDate;
2184
#$rec->{'startdate'} = $ev->{'date'};
2187
$rec->{'enddate'} = $endDate;
2189
$rec->{'enddate'} = time;
2192
# Add stats to host entry
2193
if ($ev->{'sev'} && $ev->{'sev'} ne "U") {
2195
$rec->{'sevHi'} = $ev->{'sev'};
2196
$rec->{'sevTotal'} = $ev->{'sev'};
2197
$rec->{'sevNum'} = 1;
2198
$rec->{'unknown'} = 0;
2201
$rec->{'sevHi'} = 0;
2202
$rec->{'sevTotal'} = 0;
2203
$rec->{'sevNum'} = 0;
2204
$rec->{'unknown'} = 1;
2207
# Start sdmode stats
2208
$rec->{'numPermits'} = 0;
2209
$rec->{'numRejects'} = 0;
2210
$rec->{'numAudits'} = 0;
2211
$rec->{'numEvents'} = 1; # tally all events reported for host
2213
if ($ev->{'sdmode'}) {
2214
if ($ev->{'sdmode'} =~ /PERMIT/) { $rec->{'numPermits'}++; }
2215
if ($ev->{'sdmode'} =~ /REJECT/) { $rec->{'numRejects'}++; }
2216
if ($ev->{'sdmode'} =~ /AUDIT/) { $rec->{'numAudits'}++; }
2219
push(@hostDb, $rec); # Add new records to host data list
2223
next; # Missing host info -- big problem
2225
} # END @eventDb loop
2227
# Process simple REJECT-related stats (for Executive Security Summaries)
2230
# In the end, we want this info:
2231
# - Hostname, Startdate, Enddate, # Events, # Rejects, Ave. Severity, High Severity
2233
if ($_->{'sevTotal'} > 0 && $_->{'sevNum'} > 0) {
2234
$_->{'sevMean'} = Immunix::Reports::round($_->{'sevTotal'} / $_->{'sevNum'});
2236
$_->{'sevMean'} = 0;
2240
if ($_->{'startdate'} !~ /:/) {
2241
$_->{'startdate'} = Immunix::Reports::getDate($startDate);
2243
if ($_->{'enddate'} !~ /:/) {
2244
$_->{'enddate'} = Immunix::Reports::getDate($_->{'enddate'});
2247
# Delete stuff that we may use in later versions (YaST is a silly, silly data handler)
2248
delete($_->{'sevTotal'});
2249
delete($_->{'sevNum'});
2250
delete($_->{'numPermits'});
2251
delete($_->{'numAudits'});
2252
delete($_->{'unknown'});