~ubuntu-branches/ubuntu/wily/apparmor/wily

« back to all changes in this revision

Viewing changes to utils/Reports.pm

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook
  • Date: 2011-04-27 10:38:07 UTC
  • mfrom: (5.1.118 natty)
  • Revision ID: james.westby@ubuntu.com-20110427103807-ym3rhwys6o84ith0
Tags: 2.6.1-2
debian/copyright: clarify for some full organization names.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# $Id: Reports.pm 462 2007-03-21 17:49:39Z jmichael-at-suse-de $
2
 
# ------------------------------------------------------------------
3
 
#
4
 
#    Copyright (C) 2005-2006 Novell/SUSE
5
 
#
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.
9
 
#
10
 
# ------------------------------------------------------------------
11
 
 
12
 
package Immunix::Reports;
13
 
 
14
 
################################################################################
15
 
# /usr/lib/perl5/site_perl/Reports.pm
16
 
#
17
 
#   - Parses /var/log/messages for SubDomain messages
18
 
#   - Writes results to .html or comma-delimited (.csv) files (Optional)
19
 
#
20
 
#  Requires:
21
 
#   Immunix::Events;
22
 
#   Time::Local (temporary)
23
 
#
24
 
#  Input (Optional):
25
 
#       -Start Date|End Date (Month, Day, Year, Time)
26
 
#       -Program Name
27
 
#       -Profile Name
28
 
#       -PID
29
 
#       -Denied Resources
30
 
#
31
 
################################################################################
32
 
 
33
 
use strict;
34
 
 
35
 
use DBI;
36
 
use DBD::SQLite;
37
 
use Locale::gettext;
38
 
use POSIX;
39
 
use ycp;
40
 
 
41
 
setlocale(LC_MESSAGES, "");
42
 
textdomain("Reports");
43
 
 
44
 
my $eventDb   = '/var/log/apparmor/events.db';
45
 
my $numEvents = 1000;
46
 
 
47
 
sub month2Num {
48
 
    my $lexMon = shift;
49
 
    my $months = {
50
 
        "Jan" => '01',
51
 
        "Feb" => '02',
52
 
        "Mar" => '03',
53
 
        "Apr" => '04',
54
 
        "May" => '05',
55
 
        "Jun" => '06',
56
 
        "Jul" => '07',
57
 
        "Aug" => '08',
58
 
        "Sep" => '09',
59
 
        "Oct" => '10',
60
 
        "Nov" => '11',
61
 
        "Dec" => '12'
62
 
    };
63
 
 
64
 
    my $numMonth = $months->{$lexMon};
65
 
 
66
 
    return $numMonth;
67
 
}
68
 
 
69
 
sub num2Month {
70
 
    my $monthNum = shift;
71
 
 
72
 
    my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
73
 
    my $lexMonth = $months[ ($monthNum - 1) ];
74
 
 
75
 
    return $lexMonth;
76
 
}
77
 
 
78
 
# Converts Epoch Time to Formatted Date String
79
 
sub getDate {
80
 
    my $epTime = shift;
81
 
 
82
 
    my $date = localtime($epTime);
83
 
 
84
 
    my ($day, $mon, $mondate, $time, $year) = split(/\s+/, $date);
85
 
    my ($hour, $min, $sec) = split(/:/, $time);
86
 
 
87
 
    $mon = month2Num($mon);
88
 
 
89
 
    # we want 2 digits for easier reading
90
 
    $mon     = sprintf("%02d", $mon);
91
 
    $mondate = sprintf("%02d", $mondate);
92
 
 
93
 
    my $newDate = "$year-$mon-$mondate $time";
94
 
    return $newDate;
95
 
}
96
 
 
97
 
sub round {
98
 
    my $num = shift;
99
 
    $num = sprintf("%.2f", $num);
100
 
    return ("$num");
101
 
}
102
 
 
103
 
# round up
104
 
sub pageRound {
105
 
    my $num  = shift;
106
 
    my $pnum = int($num);
107
 
 
108
 
    if ($pnum < $num) {
109
 
        $pnum++;
110
 
    }
111
 
 
112
 
    return $pnum;
113
 
}
114
 
 
115
 
sub checkFileExists {
116
 
    my $file = shift;
117
 
 
118
 
    if ($file && -e $file) {
119
 
        return 1;
120
 
    } else {
121
 
        return 0;
122
 
    }
123
 
}
124
 
 
125
 
# Translate mode & sdmode for parsing
126
 
sub rewriteModes {
127
 
    my $filts = shift;
128
 
 
129
 
    # Mode wrangling - Rewrite for better matches
130
 
    if ($filts->{'mode'} && $filts->{'mode'} ne "All") {
131
 
 
132
 
        my @mode    = ();
133
 
        my $tmpMode = undef;
134
 
 
135
 
        @mode = split(//, $filts->{'mode'});
136
 
 
137
 
        if (@mode > 0) {
138
 
            $tmpMode = join("|", @mode);
139
 
        } else {
140
 
            delete($filts->{'mode'});
141
 
        }
142
 
 
143
 
        if ($tmpMode) {
144
 
            $filts->{'mode'} = $tmpMode;
145
 
        }
146
 
    }
147
 
 
148
 
    # Rewrite sdmode for more flexible matches
149
 
    if ($filts->{'sdmode'} && $filts->{'sdmode'} ne "All") {
150
 
        my @tmpMode = ();
151
 
        if ($filts->{'sdmode'} =~ /[pP]/) { push(@tmpMode, 'PERMIT'); }
152
 
        if ($filts->{'sdmode'} =~ /[rR]/) { push(@tmpMode, 'REJECT'); }
153
 
        if ($filts->{'sdmode'} =~ /[aA]/) { push(@tmpMode, 'AUDIT'); }
154
 
        if (@tmpMode > 0) {
155
 
            $filts->{'sdmode'} = join('|', @tmpMode);
156
 
        } else {
157
 
            delete($filts->{'sdmode'});
158
 
        }
159
 
    }
160
 
 
161
 
    return $filts;
162
 
}
163
 
 
164
 
sub enableEventD {
165
 
 
166
 
    # make sure the eventd is enabled before we do any reports
167
 
    my $need_enable = 0;
168
 
    if (open(SDCONF, "/etc/apparmor/subdomain.conf")) {
169
 
        while (<SDCONF>) {
170
 
            if (/^\s*APPARMOR_ENABLE_AAEVENTD\s*=\s*(\S+)\s*$/) {
171
 
                my $flag = lc($1);
172
 
 
173
 
                # strip quotes from the value if present
174
 
                $flag = $1 if $flag =~ /^"(\S+)"$/;
175
 
                $need_enable = 1 if $flag ne "yes";
176
 
            }
177
 
        }
178
 
        close(SDCONF);
179
 
    }
180
 
 
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
185
 
    if ($need_enable) {
186
 
        my $old = "/etc/apparmor/subdomain.conf";
187
 
        my $new = "/etc/apparmor/subdomain.conf.$$";
188
 
        if (open(SDCONF, $old)) {
189
 
            if (open(SDCONFNEW, ">$new")) {
190
 
                my $foundit = 0;
191
 
 
192
 
                while (<SDCONF>) {
193
 
                    if (/^\s*APPARMOR_ENABLE_AAEVENTD\s*=/) {
194
 
                        print SDCONFNEW "APPARMOR_ENABLE_AAEVENTD=\"yes\"\n";
195
 
 
196
 
                        $foundit = 1;
197
 
                    } else {
198
 
                        print SDCONFNEW;
199
 
                    }
200
 
                }
201
 
 
202
 
                unless ($foundit) {
203
 
                    print SDCONFNEW "APPARMOR_ENABLE_AAEVENTD=\"yes\"\n";
204
 
                }
205
 
 
206
 
                close(SDCONFNEW);
207
 
 
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");
214
 
                    } else {
215
 
                        system("/sbin/rcapparmor restart >/dev/null 2>&1");
216
 
                    }
217
 
                }
218
 
            }
219
 
            close(SDCONF);
220
 
        }
221
 
 
222
 
    }
223
 
 
224
 
    return $need_enable;
225
 
}
226
 
 
227
 
# Check that events db exists and is populated
228
 
#       - Returns 1 for good db, 0 for bad db
229
 
sub checkEventDb {
230
 
    my $count   = undef;
231
 
    my $eventDb = '/var/log/apparmor/events.db';
232
 
 
233
 
    # make sure the event daemon is enabled
234
 
    if (enableEventD()) {
235
 
 
236
 
        my $now = time;
237
 
 
238
 
        # wait until the event db appears or we hit 1 min
239
 
        while (!-e $eventDb) {
240
 
            sleep 2;
241
 
            return 0 if ((time - $now) >= 60);
242
 
        }
243
 
 
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) {
249
 
            sleep 10;
250
 
            last if ((time - $now) >= 60);
251
 
            $last_modified = $modified;
252
 
            $modified      = (stat($eventDb))[9];
253
 
        }
254
 
    }
255
 
 
256
 
    my $query = "SELECT count(*) FROM events ";
257
 
 
258
 
    # Pull stuff from db
259
 
    my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 });
260
 
 
261
 
    eval {
262
 
        my $sth = $dbh->prepare($query);
263
 
        $sth->execute;
264
 
        $count = $sth->fetchrow_array();
265
 
 
266
 
        $sth->finish;
267
 
    };
268
 
 
269
 
    if ($@) {
270
 
        ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
271
 
        return;
272
 
    }
273
 
 
274
 
    $dbh->disconnect();
275
 
 
276
 
    if ($count && $count > 0) {
277
 
        return 1;
278
 
    } else {
279
 
        return 0;
280
 
    }
281
 
}
282
 
 
283
 
# Called from ag_reports_parse
284
 
sub getNumPages {
285
 
    my $args     = shift;
286
 
 
287
 
    my $db       = ();
288
 
    my $numPages = 0;
289
 
    my $count    = 0;
290
 
    my $type     = undef;
291
 
    my $eventRep = "/var/log/apparmor/reports/events.rpt";
292
 
 
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') {
296
 
            $type = 'db';
297
 
        } elsif ($args->{'type'} eq 'ess') {
298
 
            return 1;    # ess reports have one page by definition
299
 
        } else {
300
 
            $type = 'arch';    # archived or file
301
 
        }
302
 
    }
303
 
 
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;
310
 
 
311
 
        if ($args->{'sdmode'} eq "All") {
312
 
            delete($args->{'sdmode'});
313
 
        }
314
 
    }
315
 
 
316
 
    if ($args->{'mode'}) {
317
 
        $args->{'mode'} =~ s/\&//g;
318
 
        $args->{'mode'} =~ s/Mode\://g;
319
 
        $args->{'mode'} =~ s/\s//g;
320
 
 
321
 
        if ($args->{'mode'} eq "All") {
322
 
            delete($args->{'mode'});
323
 
        }
324
 
    }
325
 
    ########################################
326
 
 
327
 
    $args = rewriteModes($args);
328
 
 
329
 
    if ($type && $type eq 'db') {
330
 
 
331
 
        my $start = undef;
332
 
        my $end   = undef;
333
 
 
334
 
        if ($args->{'startTime'} && $args->{'startTime'} > 0) {
335
 
            $start = $args->{'startTime'};
336
 
        }
337
 
 
338
 
        if ($args->{'endTime'} && $args->{'endTime'} > 0) {
339
 
            $end = $args->{'endTime'};
340
 
        }
341
 
 
342
 
        my $query = "SELECT count(*) FROM events ";
343
 
 
344
 
        # We need filter information for getting a correct count
345
 
        #my $filts = getSirFilters($args);                                      # these should be sent from YaST
346
 
        my $filts = undef;
347
 
 
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'}; }
355
 
 
356
 
        for (sort(keys(%$filts))) {
357
 
            if ($filts->{$_} eq '-' || $filts->{$_} eq 'All') {
358
 
                delete($filts->{$_});
359
 
            }
360
 
        }
361
 
 
362
 
        my $midQuery = getQueryFilters($filts, $start, $end);
363
 
 
364
 
        $query .= "$midQuery";
365
 
 
366
 
        # Pull stuff from db
367
 
        my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 });
368
 
 
369
 
        eval {
370
 
            my $sth = $dbh->prepare($query);
371
 
            $sth->execute;
372
 
            $count = $sth->fetchrow_array();
373
 
 
374
 
            $sth->finish;
375
 
        };
376
 
 
377
 
        if ($@) {
378
 
            ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
379
 
            return;
380
 
        }
381
 
 
382
 
        $dbh->disconnect();
383
 
 
384
 
        #ycp::y2milestone("Numpages Query: $query");            # debug
385
 
 
386
 
        $numPages = pageRound($count / $numEvents);
387
 
        if ($numPages < 1) { $numPages = 1; }
388
 
 
389
 
    } elsif ($type && $type eq 'arch') {
390
 
 
391
 
        if (open(REP, "<$eventRep")) {
392
 
 
393
 
            while (<REP>) {
394
 
                if (/^Page/) {
395
 
                    $numPages++;
396
 
                } else {
397
 
                    $count++;
398
 
                }
399
 
            }
400
 
 
401
 
            close REP;
402
 
 
403
 
        } else {
404
 
            ycp::y2error(sprintf(gettext("Couldn't open file: %s."), $eventRep));
405
 
        }
406
 
 
407
 
    } else {
408
 
        ycp::y2error(gettext("No type value passed.  Unable to determine page count."));
409
 
        return ("1");
410
 
    }
411
 
 
412
 
    if ($numPages < 1) { $numPages = 1; }
413
 
 
414
 
    my $numCheck = int($count / $numEvents);
415
 
 
416
 
    if ($numPages < $numCheck) {
417
 
        $numPages = $numCheck;
418
 
    }
419
 
 
420
 
    return ($numPages);
421
 
}
422
 
 
423
 
sub getEpochFromNum {
424
 
    my $date = shift;
425
 
    my $place = shift || undef;    # Used to set default $sec if undef
426
 
 
427
 
    my ($numMonth, $numDay, $time, $year) = split(/\s+/, $date);
428
 
    my ($hour, $min, $sec) = '0';
429
 
    my $junk = undef;
430
 
 
431
 
    if ($time =~ /:/) {
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') {
437
 
                $sec = '59';
438
 
            } else {
439
 
                $sec = '0';
440
 
            }
441
 
        }
442
 
    }
443
 
 
444
 
    $numMonth--;    # Months start from 0 for epoch translation
445
 
 
446
 
    if (!$year) { $year = (split(/\s+/, localtime))[4]; }
447
 
    my $epochDate = timelocal($sec, $min, $hour, $numDay, $numMonth, $year);
448
 
 
449
 
    return $epochDate;
450
 
}
451
 
 
452
 
sub getEpochFromStr {
453
 
    my $lexDate = shift;
454
 
 
455
 
    my ($lexMonth, $dateDay, $fullTime, $year) = split(/\s+/, $lexDate);
456
 
 
457
 
    #my ($lexDay, $lexMonth, $dateDay, $fullTime, $year) = split(/\s+/, $lexDate);
458
 
    my ($hour, $min, $sec) = split(/\:/, $fullTime);
459
 
 
460
 
    if (!$year) { $year = (split(/\s+/, localtime))[4]; }
461
 
 
462
 
    my $numMonth = month2Num($lexMonth);
463
 
 
464
 
    my $epochDate = timelocal($sec, $min, $hour, $dateDay, $numMonth, $year);
465
 
 
466
 
    return $epochDate;
467
 
}
468
 
 
469
 
# Replaces old files with new files
470
 
sub updateFiles {
471
 
    my ($oldFile, $newFile) = @_;
472
 
 
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));
477
 
                return 1;
478
 
            }
479
 
        }
480
 
    } else {
481
 
        system('/bin/rm', "$oldFile");
482
 
        system('/bin/mv', "$newFile", "$oldFile");
483
 
    }
484
 
 
485
 
    return 0;
486
 
}
487
 
 
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) = @_;
492
 
 
493
 
    my $date = localtime;
494
 
    open(LOG, ">$logFile") || die "Couldn't open $logFile";
495
 
 
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";
499
 
    print LOG "\n";
500
 
 
501
 
    for (sort (@$db)) {
502
 
        print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},";
503
 
        print LOG "$_->{'pid'},$_->{'severity'},$->{'mode'},$_->{'resource'},$_->{'sdmode'}\n";
504
 
    }
505
 
 
506
 
    close LOG;
507
 
}
508
 
 
509
 
sub exportLog {
510
 
    my ($exportLog, $db, $header) = @_;
511
 
 
512
 
    if (open(LOG, ">$exportLog")) {
513
 
 
514
 
        my $date = localtime();
515
 
 
516
 
        if ($exportLog =~ /csv/) {
517
 
 
518
 
            # $header comes from reportgen.pl (scheduled reports)
519
 
            if ($header) { print LOG "$header\n\n"; }
520
 
 
521
 
            for (@$db) {
522
 
 
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";
527
 
 
528
 
            }
529
 
 
530
 
        } elsif ($exportLog =~ /html/) {
531
 
 
532
 
            print LOG "<html><body bgcolor='fffeec'>\n\n";
533
 
            print LOG "<font face='Helvetica,Arial,Sans-Serif'>\n";
534
 
 
535
 
            # $header comes from reportgen.pl (scheduled reports)
536
 
            if ($header) {
537
 
                print LOG "$header\n\n";
538
 
            } else {
539
 
                print LOG "<br><h3>$exportLog</h3><br>\n<h4>Log generated by Novell AppArmor, $date</h4>\n\n";
540
 
            }
541
 
 
542
 
            print LOG "<hr><br><table border='1' cellpadding='2'>\n";
543
 
 
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";
547
 
 
548
 
            my $idx = 1;
549
 
 
550
 
            for (@$db) {
551
 
                $idx++;
552
 
                if ($idx % 2 == 0) {
553
 
 
554
 
                    #"<td>&nbsp;$_->{'time'}&nbsp;</td>" .
555
 
                    print LOG "<tr><td>&nbsp;$_->{'host'}&nbsp;</td>"
556
 
                      . "<td>&nbsp;$_->{'date'}&nbsp;</td>"
557
 
                      . "<td>&nbsp;$_->{'prog'}&nbsp;</td>"
558
 
                      . "<td>&nbsp;$_->{'profile'}&nbsp;</td>"
559
 
                      . "<td>&nbsp;$_->{'pid'}&nbsp;</td>"
560
 
                      . "<td>&nbsp;$_->{'severity'}&nbsp;</td>"
561
 
                      . "<td>&nbsp;$_->{'mode'}&nbsp;</td>"
562
 
                      . "<td>&nbsp;$_->{'resource'}&nbsp;</td>"
563
 
                      . "<td>&nbsp;$_->{'sdmode'}&nbsp;</td></tr>\n";
564
 
 
565
 
                } else {
566
 
 
567
 
                    # Shade every other row
568
 
                    print LOG "<tr='edefef'><td>&nbsp;$_->{'host'}&nbsp;</td>"
569
 
                      . "<td>&nbsp;$_->{'date'}&nbsp;</td>"
570
 
                      . "<td>&nbsp;$_->{'prog'}&nbsp;</td>"
571
 
                      . "<td>&nbsp;$_->{'profile'}&nbsp;</td>"
572
 
                      . "<td>&nbsp;$_->{'pid'}&nbsp;</td>"
573
 
                      . "<td>&nbsp;$_->{'severity'}&nbsp;</td>"
574
 
                      . "<td>&nbsp;$_->{'mode'}&nbsp;</td>"
575
 
                      . "<td>&nbsp;$_->{'resource'}&nbsp;</td>"
576
 
                      . "<td>&nbsp;$_->{'sdmode'}&nbsp;</td></tr>\n";
577
 
 
578
 
                }
579
 
            }
580
 
 
581
 
            print LOG "<br></table></font></body></html>\n\n";
582
 
        }
583
 
 
584
 
        close LOG;
585
 
    } else {
586
 
        ycp::y2error(sprintf(gettext("Export Log Error: Couldn't open %s"), $exportLog));
587
 
    }
588
 
 
589
 
    # return($error);
590
 
}
591
 
 
592
 
# Pulls info on single report from apparmor xml file
593
 
sub getXmlReport {
594
 
    my ($repName, $repConf) = @_;
595
 
 
596
 
    my $repFlag = 0;
597
 
    my %rep     = ();
598
 
 
599
 
    if (defined($repName) && ref($repName)) {
600
 
 
601
 
        if ($repName->{'base'}) {
602
 
            $repName = $repName->{'base'};
603
 
        } elsif ($repName->{'name'}) {
604
 
            $repName = $repName->{'name'};
605
 
        }
606
 
    }
607
 
 
608
 
    if (!$repName) {
609
 
        ycp::y2error(gettext("Fatal error.  No report name given. Exiting."));
610
 
    }
611
 
 
612
 
    if (!$repConf || !-e $repConf) {
613
 
        $repConf = '/etc/apparmor/reports.conf';
614
 
        if (!-e $repConf) {
615
 
            ycp::y2error(
616
 
                sprintf(
617
 
                    gettext(
618
 
                        "Unable to get configuration info for %s.
619
 
                Unable to find %s."
620
 
                    ),
621
 
                    $repName,
622
 
                    $repConf
623
 
                )
624
 
            );
625
 
            exit 1;
626
 
        }
627
 
    }
628
 
 
629
 
    if (open(XML, "<$repConf")) {
630
 
 
631
 
        while (<XML>) {
632
 
 
633
 
            chomp;
634
 
 
635
 
            if (/\<name\>/) {
636
 
 
637
 
                #my $name = (split(/\"/, $_))[1];
638
 
                /\<name\>(.+)\<\/name\>/;
639
 
                my $name = $1;
640
 
                if ($name eq $repName) {
641
 
                    $rep{'name'} = $name;
642
 
                    $repFlag = 1;
643
 
                }
644
 
 
645
 
            } elsif (/\<\/report\>/) {
646
 
 
647
 
                $repFlag = 0;
648
 
 
649
 
            } elsif ($repFlag == 1) {
650
 
                if (/\s*\<\w+\s+(.*)\/\>.*$/) {
651
 
                    my $attrs = $1;
652
 
                    chomp($attrs);
653
 
                    my @attrlist = split(/\s+/, $attrs);
654
 
                    for (@attrlist) {
655
 
 
656
 
                        #Match attributes
657
 
                        if (/\s*(\S+)=\"(\S+)\"/) {
658
 
                            $rep{$1} = $2 unless $2 eq '-';
659
 
                        }
660
 
                    }
661
 
                } elsif (/\<(\w+)\>([\w+|\/].*)\<\//) {
662
 
 
663
 
                    if ($1) {
664
 
                        $rep{"$1"} = $2 unless $2 eq '-';
665
 
                    } else {
666
 
                        ycp::y2error(sprintf(gettext("Failed to parse: %s."), $_));
667
 
                    }
668
 
                }
669
 
            }
670
 
        }
671
 
 
672
 
        close XML;
673
 
 
674
 
    } else {
675
 
        ycp::y2error(sprintf(gettext("Fatal Error.  Couldn't open %s."), $repConf));
676
 
        exit 1;
677
 
    }
678
 
 
679
 
    return \%rep;
680
 
}
681
 
 
682
 
# Returns info on currently confined processes
683
 
sub getCfInfo {
684
 
 
685
 
    my $ref  = ();
686
 
    my @cfDb = ();
687
 
 
688
 
    my $cfApp = '/usr/sbin/unconfined';
689
 
 
690
 
    if (open(CF, "$cfApp |")) {
691
 
 
692
 
        my $host = `hostname`;
693
 
        chomp($host);
694
 
 
695
 
        my $date = localtime;
696
 
 
697
 
        while (<CF>) {
698
 
 
699
 
            my $ref = ();
700
 
            my $all = undef;
701
 
            $ref->{'host'} = $host;
702
 
            $ref->{'date'} = $date;
703
 
            chomp;
704
 
 
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+/, $_);
711
 
 
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'}  = "-"; }
717
 
 
718
 
            push(@cfDb, $ref);
719
 
        }
720
 
        close CF;
721
 
 
722
 
    } else {
723
 
        my $error = sprintf(gettext("Fatal Error.  Can't run %s.  Exiting."), $cfApp);
724
 
        ycp::y2error($error);
725
 
        return $error;
726
 
    }
727
 
 
728
 
    return (\@cfDb);
729
 
}
730
 
 
731
 
# generate stats for ESS reports
732
 
sub getEssStats {
733
 
    my $args = shift;
734
 
 
735
 
    #my ($host, $targetDir, $startdate, $enddate) = @_;
736
 
 
737
 
    my @hostDb    = ();
738
 
    my @hostList  = ();
739
 
    my $targetDir = undef;
740
 
    my $host      = undef;
741
 
    my $startdate = undef;
742
 
    my $enddate   = undef;
743
 
 
744
 
    if (!$args->{'targetDir'}) {
745
 
        $targetDir = '/var/log/apparmor/';
746
 
    }
747
 
 
748
 
    if ($args->{'host'}) { $host = $args->{'host'}; }
749
 
 
750
 
    if ($args->{'startdate'}) {
751
 
        $startdate = $args->{'startdate'};
752
 
    } else {
753
 
        $startdate = '1104566401';    # Jan 1, 2005
754
 
    }
755
 
 
756
 
    if ($args->{'enddate'}) {
757
 
        $enddate = $args->{'enddate'};
758
 
    } else {
759
 
        $enddate = time;
760
 
    }
761
 
 
762
 
    if (!-e $targetDir) {
763
 
        ycp::y2error(sprintf(gettext("Fatal Error.  No directory, %s, found.  Exiting."), $targetDir));
764
 
        return;
765
 
    }
766
 
 
767
 
    # Max Sev, Ave. Sev, Num. Rejects, Start Time, End Time
768
 
    my $ctQuery = "SELECT count(*) FROM events WHERE time >= $startdate AND time <= $enddate";
769
 
 
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";
773
 
 
774
 
#                "MAX(time) FROM events join info WHERE sdmode='REJECTING' AND " .
775
 
 
776
 
    # Get list of hosts to scan
777
 
    if (opendir(TDIR, $targetDir)) {
778
 
 
779
 
        @hostList = grep(/\.db/, readdir(TDIR));
780
 
        close TDIR;
781
 
 
782
 
    } else {
783
 
        ycp::y2error(sprintf(gettext("Fatal Error.  Couldn't open %s.  Exiting"), $targetDir));
784
 
        return;
785
 
    }
786
 
 
787
 
    # Cycle through for each host
788
 
    for my $eventDb (@hostList) {
789
 
 
790
 
        $eventDb = "$targetDir/$eventDb";
791
 
 
792
 
        my $ess   = undef;
793
 
        my $ret   = undef;
794
 
        my $count = undef;
795
 
 
796
 
        #my $eventDb = '/var/log/apparmor/events.db';
797
 
 
798
 
        my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 });
799
 
 
800
 
        # get hostname
801
 
        my $host      = undef;
802
 
        my $hostQuery = "SELECT * FROM info WHERE name='host'";
803
 
 
804
 
        eval {
805
 
            my $sth = $dbh->prepare($hostQuery);
806
 
            $sth->execute;
807
 
            $host = $sth->fetchrow_array();
808
 
            $sth->finish;
809
 
        };
810
 
 
811
 
        if ($@) {
812
 
            ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr));
813
 
            return;
814
 
        }
815
 
 
816
 
        # Get number of events
817
 
        eval {
818
 
            my $sth = $dbh->prepare($ctQuery);
819
 
            $sth->execute;
820
 
            $count = $sth->fetchrow_array();
821
 
            $sth->finish;
822
 
        };
823
 
 
824
 
        if ($@) {
825
 
            ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr));
826
 
            return;
827
 
        }
828
 
 
829
 
        # Get rest of stats
830
 
        eval { $ret = $dbh->selectall_arrayref("$query"); };
831
 
 
832
 
        if ($@) {
833
 
            ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr));
834
 
            return;
835
 
        }
836
 
 
837
 
        $dbh->disconnect();
838
 
 
839
 
        # hostIp, startDate, endDate, sevHi,  sevMean, numRejects
840
 
        if ($host) {
841
 
            $ess->{'host'} = $host;
842
 
        } else {
843
 
            $ess->{'host'} = '';
844
 
        }
845
 
 
846
 
        $ess->{'sevHi'} = $$ret[0]->[0];
847
 
 
848
 
        if (!$ess->{'sevHi'}) {
849
 
            $ess->{'sevHi'} = 0;
850
 
        }
851
 
 
852
 
        $ess->{'sevMean'} = $$ret[0]->[1];
853
 
 
854
 
        if (!$ess->{'sevMean'} || $ess->{'sevHi'} == 0) {
855
 
            $ess->{'sevMean'} = 0;
856
 
        } else {
857
 
            $ess->{'sevMean'} = round("$ess->{'sevMean'}");
858
 
        }
859
 
 
860
 
        $ess->{'numRejects'} = $$ret[0]->[2];
861
 
        $ess->{'startdate'}  = $$ret[0]->[3];
862
 
        $ess->{'enddate'}    = $$ret[0]->[4];
863
 
        $ess->{'numEvents'}  = $count;
864
 
 
865
 
        # Convert dates
866
 
        if ($ess->{'startdate'} && $ess->{'startdate'} !~ /:/) {
867
 
            $ess->{'startdate'} = Immunix::Reports::getDate($ess->{'startdate'});
868
 
        }
869
 
        if ($ess->{'enddate'} && $ess->{'enddate'} !~ /:/) {
870
 
            $ess->{'enddate'} = Immunix::Reports::getDate($ess->{'enddate'});
871
 
        }
872
 
 
873
 
        push(@hostDb, $ess);
874
 
    }
875
 
 
876
 
    return \@hostDb;
877
 
}
878
 
 
879
 
# get ESS stats for archived reports (warning -- this can be slow for large files
880
 
# debug -- not fully functional yet
881
 
sub getArchEssStats {
882
 
    my $args = shift;
883
 
 
884
 
    my $prevTime  = '0';
885
 
    my $prevDate  = '0';
886
 
    my $startDate = '1104566401';    # Jan 1, 2005
887
 
    my $endDate   = time;
888
 
 
889
 
    if ($args->{'startdate'}) { $startDate = $args->{'startdate'}; }
890
 
    if ($args->{'enddate'})   { $endDate   = $args->{'enddate'}; }
891
 
 
892
 
    # hostIp, startDate, endDate, sevHi,  sevMean, numRejects
893
 
    my @eventDb = getEvents("$startDate", "$endDate");
894
 
 
895
 
    my @hostIdx = ();    # Simple index to all hosts for quick host matching
896
 
    my @hostDb  = ();    # Host-keyed Data for doing REJECT stats
897
 
 
898
 
    # Outer Loop for Raw Event db
899
 
    for (@eventDb) {
900
 
 
901
 
        if ($_->{'host'}) {
902
 
 
903
 
            my $ev = $_;             # current event record
904
 
 
905
 
            # Create new host entry, or add to existing
906
 
            if (grep(/$ev->{'host'}/, @hostIdx) == 1) {
907
 
 
908
 
                # Inner loop, but the number of hosts should be small
909
 
                for (@hostDb) {
910
 
 
911
 
                    if ($_->{'host'} eq $ev->{'host'}) {
912
 
 
913
 
                        # Find earliest start date
914
 
                        if ($_->{'startdate'} > $ev->{'date'}) {
915
 
                            $_->{'startdate'} = $ev->{'date'};
916
 
                        }
917
 
 
918
 
                        # tally all events reported for host
919
 
                        $_->{'numEvents'}++;
920
 
 
921
 
                        if ($ev->{'sdmode'}) {
922
 
                            if ($ev->{'sdmode'} =~ /PERMIT/) {
923
 
                                $_->{'numPermits'}++;
924
 
                            }
925
 
                            if ($ev->{'sdmode'} =~ /REJECT/) {
926
 
                                $_->{'numRejects'}++;
927
 
                            }
928
 
                            if ($ev->{'sdmode'} =~ /AUDIT/) {
929
 
                                $_->{'numAudits'}++;
930
 
                            }
931
 
                        }
932
 
 
933
 
                        # Add stats to host entry
934
 
                        #if ( $ev->{'severity'} && $ev->{'severity'} =~ /\b\d+\b/ ) {}
935
 
                        if ($ev->{'severity'} && $ev->{'severity'} != -1) {
936
 
 
937
 
                            $_->{'sevNum'}++;
938
 
                            $_->{'sevTotal'} = $_->{'sevTotal'} + $ev->{'severity'};
939
 
 
940
 
                            if ($ev->{'severity'} > $_->{'sevHi'}) {
941
 
                                $_->{'sevHi'} = $ev->{'severity'};
942
 
                            }
943
 
                        } else {
944
 
                            $_->{'unknown'}++;
945
 
                        }
946
 
                    }
947
 
                }
948
 
 
949
 
            } else {
950
 
 
951
 
                # New host
952
 
                my $rec = undef;
953
 
                push(@hostIdx, $ev->{'host'});    # Add host entry to index
954
 
 
955
 
                $rec->{'host'}      = $ev->{'host'};
956
 
                $rec->{'startdate'} = $startDate;
957
 
 
958
 
                #$rec->{'startdate'} = $ev->{'date'};
959
 
 
960
 
                if ($endDate) {
961
 
                    $rec->{'enddate'} = $endDate;
962
 
                } else {
963
 
                    $rec->{'enddate'} = time;
964
 
                }
965
 
 
966
 
                # Add stats to host entry
967
 
                if ($ev->{'sev'} && $ev->{'sev'} ne "U") {
968
 
 
969
 
                    $rec->{'sevHi'}    = $ev->{'sev'};
970
 
                    $rec->{'sevTotal'} = $ev->{'sev'};
971
 
                    $rec->{'sevNum'}   = 1;
972
 
                    $rec->{'unknown'}  = 0;
973
 
 
974
 
                } else {
975
 
 
976
 
                    $rec->{'sevHi'}    = 0;
977
 
                    $rec->{'sevTotal'} = 0;
978
 
                    $rec->{'sevNum'}   = 0;
979
 
                    $rec->{'unknown'}  = 1;
980
 
 
981
 
                }
982
 
 
983
 
                # Start sdmode stats
984
 
                $rec->{'numPermits'} = 0;
985
 
                $rec->{'numRejects'} = 0;
986
 
                $rec->{'numAudits'}  = 0;
987
 
                $rec->{'numEvents'}  = 1;    # tally all events reported for host
988
 
 
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'}++; }
993
 
                }
994
 
 
995
 
                push(@hostDb, $rec);         # Add new records to host data list
996
 
            }
997
 
 
998
 
        } else {
999
 
            next;                            # Missing host info -- big problem
1000
 
        }
1001
 
    }    # END @eventDb loop
1002
 
 
1003
 
    # Process simple REJECT-related stats (for Executive Security Summaries)
1004
 
    for (@hostDb) {
1005
 
 
1006
 
# In the end, we want this info:
1007
 
#       - Hostname, Startdate, Enddate, # Events, # Rejects, Ave. Severity, High Severity
1008
 
 
1009
 
        if ($_->{'sevTotal'} > 0 && $_->{'sevNum'} > 0) {
1010
 
            $_->{'sevMean'} = round($_->{'sevTotal'} / $_->{'sevNum'});
1011
 
        } else {
1012
 
            $_->{'sevMean'} = 0;
1013
 
        }
1014
 
 
1015
 
        # Convert dates
1016
 
        if ($_->{'startdate'} !~ /:/) {
1017
 
            $_->{'startdate'} = getDate($startDate);
1018
 
        }
1019
 
        if ($_->{'enddate'} !~ /:/) {
1020
 
            $_->{'enddate'} = getDate($_->{'enddate'});
1021
 
        }
1022
 
 
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'});
1030
 
 
1031
 
    }
1032
 
 
1033
 
    return (\@hostDb);
1034
 
}
1035
 
 
1036
 
# special version of getEvents() for /usr/bin/reportgen.pl
1037
 
sub grabEvents {
1038
 
    my ($rep, $start, $end) = @_;
1039
 
 
1040
 
    my $db       = undef;
1041
 
    my $prevDate = "0";
1042
 
    my $prevTime = "0";
1043
 
 
1044
 
    my $query = "SELECT * FROM events ";
1045
 
 
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; }
1053
 
 
1054
 
    if ($rep->{'prog'} && ($rep->{'prog'} eq "-" || $rep->{'prog'} eq "All")) {
1055
 
        delete($rep->{'prog'});
1056
 
    }
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'});
1061
 
    }
1062
 
    if ($rep->{'resource'} && $rep->{'resource'} eq "-") {
1063
 
        delete($rep->{'resource'});
1064
 
    }
1065
 
 
1066
 
    if ($rep->{'mode'} && ($rep->{'mode'} eq "-" || $rep->{'mode'} eq "All")) {
1067
 
        delete($rep->{'mode'});
1068
 
    }
1069
 
 
1070
 
    if ($rep->{'sdmode'}
1071
 
        && ($rep->{'sdmode'} eq "-" || $rep->{'sdmode'} eq "All"))
1072
 
    {
1073
 
        delete($rep->{'sdmode'});
1074
 
    }
1075
 
 
1076
 
    $rep = rewriteModes($rep);
1077
 
 
1078
 
    # Set Dates far enough apart to get all entries (ie. no date filter)
1079
 
    my $startDate = '1104566401';    # Jan 1, 2005
1080
 
    my $endDate   = time;
1081
 
 
1082
 
    if ($start && $start > 0) { $startDate = $start; }
1083
 
 
1084
 
    if (ref($rep)) {
1085
 
        my $midQuery = getQueryFilters($rep, $startDate, $endDate);
1086
 
        $query .= "$midQuery";
1087
 
    }
1088
 
 
1089
 
    $db = getEvents($query, "$startDate", "$endDate");
1090
 
 
1091
 
    return ($db);
1092
 
}
1093
 
 
1094
 
sub getQueryFilters {
1095
 
    my ($filts, $start, $end) = @_;
1096
 
 
1097
 
    my $query = undef;
1098
 
    my $wFlag = 0;
1099
 
 
1100
 
    if ($filts) {
1101
 
 
1102
 
        # Match any requested filters or drop record
1103
 
        ############################################################
1104
 
        if ($filts->{'prog'}) {
1105
 
            $query .= "WHERE events.prog = \'$filts->{'prog'}\' ";
1106
 
            $wFlag = 1;
1107
 
        }
1108
 
 
1109
 
        if ($filts->{'profile'} && $_->{'profile'}) {
1110
 
            if ($wFlag == 1) {
1111
 
                $query .= "AND events.profile = \'$filts->{'profile'}\' ";
1112
 
            } else {
1113
 
                $query .= "WHERE events.profile = \'$filts->{'profile'}\' ";
1114
 
            }
1115
 
            $wFlag = 1;
1116
 
        }
1117
 
 
1118
 
        if ($filts->{'pid'}) {
1119
 
            if ($wFlag == 1) {
1120
 
                $query .= "AND events.pid = \'$filts->{'pid'}\' ";
1121
 
            } else {
1122
 
                $query .= "WHERE events.pid = \'$filts->{'pid'}\' ";
1123
 
            }
1124
 
            $wFlag = 1;
1125
 
        }
1126
 
 
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")
1132
 
            {
1133
 
                if ($wFlag == 1) {
1134
 
                    $query .= "AND events.severity = '-1' ";
1135
 
                } else {
1136
 
                    $query .= "WHERE events.severity = '-1' ";
1137
 
                }
1138
 
                $wFlag = 1;
1139
 
            } else {
1140
 
                if ($wFlag == 1) {
1141
 
                    $query .= "AND events.severity >= \'$filts->{'severity'}\' ";
1142
 
                } else {
1143
 
                    $query .= "WHERE events.severity >= \'$filts->{'severity'}\' ";
1144
 
                }
1145
 
                $wFlag = 1;
1146
 
            }
1147
 
        }
1148
 
 
1149
 
        if ($filts->{'resource'}) {
1150
 
            if ($wFlag == 1) {
1151
 
                $query .= "AND events.resource LIKE '%$filts->{'resource'}%' ";
1152
 
            } else {
1153
 
                $query .= "WHERE events.resource LIKE '%$filts->{'resource'}%' ";
1154
 
            }
1155
 
            $wFlag = 1;
1156
 
        }
1157
 
 
1158
 
        if ($filts->{'mode'}) {
1159
 
            if ($wFlag == 1) {
1160
 
                $query .= "AND events.mode LIKE '%$filts->{'mode'}%' ";
1161
 
            } else {
1162
 
                $query .= "WHERE events.mode LIKE '%$filts->{'mode'}%' ";
1163
 
            }
1164
 
            $wFlag = 1;
1165
 
        }
1166
 
 
1167
 
        if ($filts->{'sdmode'}) {
1168
 
 
1169
 
            if ($filts->{'sdmode'} =~ /\|/) {
1170
 
 
1171
 
                my @sdmunge = split(/\|/, $filts->{'sdmode'});
1172
 
                for (@sdmunge) { $_ = "\'\%" . "$_" . "\%\'"; }
1173
 
 
1174
 
                $filts->{'sdmode'} = join(" OR events.sdmode LIKE ", @sdmunge);
1175
 
 
1176
 
            } else {
1177
 
                $filts->{'sdmode'} = "\'\%" . "$filts->{'sdmode'}" . "\%\'";
1178
 
            }
1179
 
 
1180
 
            if ($wFlag == 1) {
1181
 
                $query .= "AND events.sdmode LIKE $filts->{'sdmode'} ";
1182
 
            } else {
1183
 
                $query .= "WHERE events.sdmode LIKE $filts->{'sdmode'} ";
1184
 
            }
1185
 
            $wFlag = 1;
1186
 
 
1187
 
        }
1188
 
    }
1189
 
 
1190
 
    if ($start && $start =~ /\d+/ && $start > 0) {
1191
 
        if ($wFlag == 1) {
1192
 
            $query .= "AND events.time >= $start ";
1193
 
        } else {
1194
 
            $query .= "WHERE events.time >= $start ";
1195
 
        }
1196
 
        $wFlag = 1;
1197
 
    }
1198
 
 
1199
 
    if ($end && $end =~ /\d+/ && $end > $start) {
1200
 
        if ($wFlag == 1) {
1201
 
            $query .= "AND events.time <= $end ";
1202
 
        } else {
1203
 
            $query .= "WHERE events.time <= $end ";
1204
 
        }
1205
 
    }
1206
 
 
1207
 
    return $query;
1208
 
}
1209
 
 
1210
 
sub getQuery {
1211
 
    my ($filts, $page, $sortKey, $numEvents) = @_;
1212
 
 
1213
 
    if (!$page || $page < 1 || $page !~ /\d+/) { $page = 1; }
1214
 
    if (!$sortKey)   { $sortKey   = 'time'; }
1215
 
    if (!$numEvents) { $numEvents = '1000'; }
1216
 
 
1217
 
    my $limit = (($page * $numEvents) - $numEvents);
1218
 
 
1219
 
    my $query = "SELECT * FROM events ";
1220
 
 
1221
 
    if ($filts) {
1222
 
        my $midQuery = getQueryFilters($filts);
1223
 
        $query .= "$midQuery";
1224
 
    }
1225
 
 
1226
 
    # Finish query
1227
 
    $query .= "Order by $sortKey LIMIT $limit,$numEvents";
1228
 
 
1229
 
    return $query;
1230
 
}
1231
 
 
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
1235
 
 
1236
 
sub queryPostProcess {
1237
 
    my $db       = shift;
1238
 
 
1239
 
    my @newDb    = ();
1240
 
    my $prevTime = 0;
1241
 
    my $prevDate = 0;
1242
 
 
1243
 
    for (@$db) {
1244
 
 
1245
 
        # Shuffle special events into appropriate column variables
1246
 
        ############################################################
1247
 
        if ($_->{'attrch'}) { $_->{'sdmode'} .= " $_->{'attrch'}"; }
1248
 
 
1249
 
        if ($_->{'type'}) {
1250
 
 
1251
 
            if ($_->{'type'} eq 'control_variable') {
1252
 
 
1253
 
                # OWLSM gets special treatment
1254
 
                if ($_->{'variable'} eq 'owlsm') {
1255
 
 
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";
1261
 
                    } else {
1262
 
                        $_->{'resource'} = "Unrecognized OWLSM activity.";
1263
 
                    }
1264
 
                } else {
1265
 
                    $_->{'resource'} = "$_->{'variable'}";
1266
 
                }
1267
 
            } elsif ($_->{'type'} eq 'capability') {
1268
 
                $_->{'resource'} .= " $_->{'capability'}";
1269
 
 
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";
1276
 
 
1277
 
                # DROP logprof-hints
1278
 
            } elsif ($_->{'type'} eq 'unknown_hat') {
1279
 
                next;
1280
 
 
1281
 
                # DROP logprof-hints
1282
 
            } elsif ($_->{'type'} eq 'changing_profile') {
1283
 
                next;
1284
 
 
1285
 
                # DROP logprof-hints
1286
 
            } elsif ($_->{'type'} eq 'fork') {
1287
 
                next;
1288
 
            } elsif ($_->{'type'} ne 'path') {
1289
 
                $_->{'resource'} .= " $_->{'type'}";
1290
 
            }
1291
 
        }
1292
 
 
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'};
1301
 
        } else {
1302
 
            $_->{'date'} = "0000-00-00 00:00:00";
1303
 
        }
1304
 
 
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'}   = "-"; }
1314
 
 
1315
 
        if (!$_->{'severity'}) {
1316
 
            $_->{'severity'} = "-";
1317
 
        } elsif ($_->{'severity'} eq "-1") {
1318
 
            $_->{'severity'} = "U";
1319
 
        }    # else {
1320
 
             #   $_->{'severity'} = sprintf("%02d", $_->{'severity'});
1321
 
             #}
1322
 
 
1323
 
        push(@newDb, $_);    # Don't quote the $_ (breaks hash)
1324
 
 
1325
 
    }
1326
 
 
1327
 
    return \@newDb;
1328
 
}
1329
 
 
1330
 
# Creates single hashref for the various filters
1331
 
sub setFormFilters {
1332
 
    my $args  = shift;
1333
 
 
1334
 
    my $filts = undef;
1335
 
 
1336
 
    if ($args) {
1337
 
 
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'}; }
1345
 
 
1346
 
    }
1347
 
 
1348
 
    return $filts;
1349
 
}
1350
 
 
1351
 
# helper for getSirFilters()
1352
 
# Makes gui-centric filters querying-friendly
1353
 
sub rewriteFilters {
1354
 
    my $filts = shift;
1355
 
 
1356
 
    # Clear unnecessary filters
1357
 
    for (keys(%$filts)) {
1358
 
        if ($filts->{$_} eq "All") { delete($filts->{$_}); }
1359
 
    }
1360
 
 
1361
 
    if ($filts->{'prog'}
1362
 
        && ($filts->{'prog'} eq "-" || $filts->{'prog'} eq "All"))
1363
 
    {
1364
 
        delete($filts->{'prog'});
1365
 
    }
1366
 
    if ($filts->{'profile'} && ($filts->{'profile'} eq "-")) {
1367
 
        delete($filts->{'profile'});
1368
 
    }
1369
 
    if ($filts->{'pid'} && ($filts->{'pid'} eq "-")) {
1370
 
        delete($filts->{'pid'});
1371
 
    }
1372
 
    if ($filts->{'severity'} && ($filts->{'severity'} eq "-")) {
1373
 
        delete($filts->{'severity'});
1374
 
    }
1375
 
    if ($filts->{'resource'} && ($filts->{'resource'} eq "-")) {
1376
 
        delete($filts->{'resource'});
1377
 
    }
1378
 
 
1379
 
    if ($filts->{'mode'}
1380
 
        && ($filts->{'mode'} eq "-" || $filts->{'mode'} eq "All"))
1381
 
    {
1382
 
        delete($filts->{'mode'});
1383
 
    }
1384
 
 
1385
 
    if ($filts->{'sdmode'}
1386
 
        && ($filts->{'sdmode'} eq "-" || $filts->{'sdmode'} eq "All"))
1387
 
    {
1388
 
        delete($filts->{'sdmode'});
1389
 
    }
1390
 
    ############################################################
1391
 
 
1392
 
    $filts = rewriteModes($filts);
1393
 
 
1394
 
    return $filts;
1395
 
}
1396
 
 
1397
 
# returns ref to active filters for the specific SIR report
1398
 
sub getSirFilters {
1399
 
    my $args    = shift;
1400
 
 
1401
 
    my $repName = undef;
1402
 
 
1403
 
    if ($args && $args->{'name'}) {
1404
 
        $repName = $args->{'name'};
1405
 
    } else {
1406
 
        $repName = "Security.Incident.Report";
1407
 
    }
1408
 
 
1409
 
    my $repConf = '/etc/apparmor/reports.conf';
1410
 
    my $rec     = undef;
1411
 
 
1412
 
    my $filts = getXmlReport($repName);
1413
 
 
1414
 
    # Clean hash of useless refs
1415
 
    for (sort keys(%$filts)) {
1416
 
        if ($filts->{$_} eq "-") {
1417
 
            delete($filts->{$_});
1418
 
        }
1419
 
    }
1420
 
 
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'}); }
1429
 
 
1430
 
    if (!$args->{'gui'} || $args->{'gui'} ne "1") {
1431
 
        $filts = rewriteModes($filts);
1432
 
        $filts = rewriteFilters($filts);
1433
 
    }
1434
 
 
1435
 
    return $filts;
1436
 
}
1437
 
 
1438
 
# deprecated (pre-xml)
1439
 
sub OldgetSirFilters {
1440
 
    my $args    = shift;
1441
 
 
1442
 
    my $repName = undef;
1443
 
 
1444
 
    if ($args && $args->{'name'}) {
1445
 
        $repName = $args->{'name'};
1446
 
    }
1447
 
 
1448
 
    my $repConf = '/etc/apparmor/reports.conf';
1449
 
    my $rec     = undef;
1450
 
 
1451
 
    if (!$repName) {
1452
 
        $repName = "\"Security.Incident.Report\"";
1453
 
    } else {
1454
 
        $repName = "\"$repName\"";
1455
 
    }
1456
 
 
1457
 
    if (open(CF, "<$repConf")) {
1458
 
 
1459
 
        while (<CF>) {
1460
 
            next if /^#/;
1461
 
            chomp;
1462
 
            my ($cfRptName) = (split(/:/, $_))[0];
1463
 
            $cfRptName =~ s/\s+$//;    # remove trailing spaces
1464
 
 
1465
 
            next unless ($cfRptName eq "$repName");
1466
 
 
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);
1469
 
            $info =~ s/\s+//g;
1470
 
            $name =~ s/^\s+//;
1471
 
            $name =~ s/\s+$//;
1472
 
            my ($mailtype, $filters, $email, $lastRun) =
1473
 
              split(/\s*:\s*/, $info, 4);
1474
 
 
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);
1479
 
 
1480
 
        }
1481
 
 
1482
 
        close CF;
1483
 
    } else {
1484
 
        logError("Couldn't open $repConf.  No filters will be used in report generation.");
1485
 
        return;
1486
 
    }
1487
 
 
1488
 
    # Clean hash of useless refs
1489
 
    for (sort keys(%$rec)) {
1490
 
        if ($rec->{$_} eq "-") {
1491
 
            delete($rec->{$_});
1492
 
        }
1493
 
    }
1494
 
 
1495
 
    $rec = rewriteModes($rec);
1496
 
 
1497
 
    if (!$args->{'gui'} || $args->{'gui'} ne "1") {
1498
 
        $rec = rewriteFilters($rec);
1499
 
    }
1500
 
 
1501
 
    return $rec;
1502
 
}
1503
 
 
1504
 
# Main SIR report generator
1505
 
sub getEvents {
1506
 
    my ($query, $start, $end, $dbFile) = @_;
1507
 
 
1508
 
    my @events   = ();
1509
 
    my $prevTime = 0;
1510
 
    my $prevDate = '0';
1511
 
 
1512
 
    if (!$query || $query !~ /^SELECT/) { $query = "SELECT * FROM events"; }
1513
 
    if ($dbFile && -f $dbFile) { $eventDb = $dbFile; }
1514
 
 
1515
 
    my $hostName = `/bin/hostname` || 'unknown';
1516
 
    chomp $hostName unless $hostName eq 'unknown';
1517
 
 
1518
 
    if (!$start) { $start = '1104566401'; }    # Give default start of 1/1/2005
1519
 
    if (!$end)   { $end   = time; }
1520
 
 
1521
 
    # make sure they don't give us a bad range
1522
 
    ($start, $end) = ($end, $start) if $start > $end;
1523
 
 
1524
 
# Events Schema
1525
 
# - (id,time,counter,pid,sdmode,type,mode,resource,target,profile,prog,severity);
1526
 
 
1527
 
    # Pull stuff from db
1528
 
    my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", { RaiseError => 1, AutoCommit => 1 });
1529
 
    my $all = undef;
1530
 
    eval { $all = $dbh->selectall_arrayref("$query"); };
1531
 
 
1532
 
    if ($@) {
1533
 
        ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
1534
 
        return;
1535
 
    }
1536
 
 
1537
 
    $dbh->disconnect();
1538
 
 
1539
 
    for my $row (@$all) {
1540
 
        my $rec = undef;
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;
1542
 
 
1543
 
        # Give empty record values a default value
1544
 
        if (!$rec->{'host'}) { $rec->{'host'} = $hostName; }
1545
 
        for (keys(%$rec)) {
1546
 
            if (!$rec->{$_}) { $rec->{$_} = '-'; }
1547
 
        }
1548
 
 
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'};
1557
 
        } else {
1558
 
            $rec->{'date'} = "0000-00-00-00:00:00";
1559
 
        }
1560
 
 
1561
 
        if ($rec->{'severity'} && $rec->{'severity'} eq '-1') {
1562
 
            $rec->{'severity'} = 'U';
1563
 
        }
1564
 
 
1565
 
        delete($rec->{'time'});
1566
 
        delete($rec->{'counter'});
1567
 
 
1568
 
        push(@events, $rec);
1569
 
    }
1570
 
 
1571
 
    return \@events;
1572
 
}
1573
 
 
1574
 
# Archived Reports Stuff -- Some of this would go away in an ideal world
1575
 
################################################################################
1576
 
sub getArchReport {
1577
 
    my $args     = shift;
1578
 
 
1579
 
    my @rec      = ();
1580
 
    my $eventRep = "/var/log/apparmor/reports/events.rpt";
1581
 
 
1582
 
    #if ( $args->{'type'} && $args->{'type'} eq 'archRep' ) {}
1583
 
    if ($args->{'logFile'}) {
1584
 
        $eventRep = $args->{'logFile'};
1585
 
    }
1586
 
 
1587
 
    if (open(REP, "<$eventRep")) {
1588
 
 
1589
 
        my $page = 1;
1590
 
 
1591
 
        if ($args->{'page'}) { $page = $args->{'page'}; }
1592
 
 
1593
 
        my $id    = 1;
1594
 
        my $slurp = 0;
1595
 
 
1596
 
        #my $numPages = 0;
1597
 
 
1598
 
        my $prevTime = undef;
1599
 
        my $prevDate = undef;
1600
 
 
1601
 
        while (<REP>) {
1602
 
 
1603
 
            my $db = ();
1604
 
 
1605
 
            # Why not get rid of page and just do divide by $i later?
1606
 
            if (/Page/) {
1607
 
 
1608
 
                #               $numPages++;
1609
 
                chomp;
1610
 
                if ($_ eq "Page $page") {
1611
 
                    $slurp = 1;
1612
 
                } else {
1613
 
                    $slurp = 0;
1614
 
                }
1615
 
            } elsif ($slurp == 1) {
1616
 
 
1617
 
                chomp;
1618
 
 
1619
 
                ($db->{'host'}, $db->{'time'}, $db->{'prog'}, $db->{'profile'}, $db->{'pid'}, $db->{'severity'}, $db->{'mode'}, $db->{'denyRes'}, $db->{'sdmode'}) = split(/\,/, $_);
1620
 
 
1621
 
                # Convert epoch time to date
1622
 
                if ($db->{'time'} == $prevTime) {
1623
 
                    $db->{'date'} = $prevDate;
1624
 
                } else {
1625
 
                    $prevTime     = $db->{'time'};
1626
 
                    $prevDate     = getDate("$db->{'time'}");
1627
 
                    $db->{'date'} = $prevDate;
1628
 
                }
1629
 
 
1630
 
                $id++;
1631
 
                $db->{'date'} = $db->{'time'};
1632
 
                delete $db->{'time'};
1633
 
                push(@rec, $db);
1634
 
            }
1635
 
        }
1636
 
 
1637
 
        close REP;
1638
 
 
1639
 
    } else {
1640
 
        ycp::y2error(sprintf(gettext("Fatal Error.  getArchReport() couldn't open %s"), $eventRep));
1641
 
        return ("Couldn't open $eventRep");
1642
 
    }
1643
 
 
1644
 
    return (\@rec);
1645
 
}
1646
 
 
1647
 
sub writeEventReport {
1648
 
    my ($db, $args) = @_;    # Filters for date, && regexp
1649
 
#    my $type = shift || undef;
1650
 
 
1651
 
    my $eventRep = "/var/log/apparmor/reports/events.rpt";
1652
 
 
1653
 
    # Not sure if this is needed anymore, but it messes up archived SIR reports
1654
 
    # if ( $args->{'logFile'} ) { $eventRep = $args->{'logFile'}; }
1655
 
 
1656
 
    if (open(REP, ">$eventRep")) {
1657
 
 
1658
 
        my $i    = 1;
1659
 
        my $page = 1;
1660
 
        my $skip = 0;
1661
 
 
1662
 
        # Title for scheduled reports
1663
 
        if ($args->{'title'}) { print REP "$args->{'title'}"; }
1664
 
 
1665
 
        print REP "Page $page\n";
1666
 
        $page++;
1667
 
 
1668
 
        for (@$db) {
1669
 
 
1670
 
            print REP "$_->{'host'},$_->{'date'},$_->{'prog'},$_->{'profile'},$_->{'pid'},$_->{'severity'},$_->{'mode'},$_->{'resource'},$_->{'sdmode'}\n";
1671
 
 
1672
 
            if (($i % $numEvents) == 0 && $skip == 0) {
1673
 
                print REP "Page $page\n";
1674
 
                $page++;
1675
 
                $skip = 1;
1676
 
            } else {
1677
 
                $i++;
1678
 
                $skip = 0;
1679
 
            }
1680
 
 
1681
 
        }
1682
 
 
1683
 
        close REP;
1684
 
 
1685
 
    } else {
1686
 
        return ("Couldn't open $eventRep");
1687
 
    }
1688
 
 
1689
 
    return 0;
1690
 
}
1691
 
 
1692
 
sub prepSingleLog {
1693
 
    my $args = shift;
1694
 
 
1695
 
    my $dir      = '/var/log/apparmor/reports-archived';
1696
 
    my $error    = "0";
1697
 
    my @errors   = ();                                             # For non-fatal errors
1698
 
    my @repList  = ();
1699
 
    my $readFile = "";
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
1702
 
 
1703
 
    if ($args->{'logFile'}) { $readFile = $args->{'logFile'}; }
1704
 
    if ($args->{'repPath'}) { $dir      = $args->{'repPath'}; }
1705
 
 
1706
 
    my @rawDb      = ();
1707
 
    my $numPages   = 1;
1708
 
    my $numRecords = 1;
1709
 
    my $skip       = 0;
1710
 
 
1711
 
    # Open record compilation file
1712
 
    if (open(RREP, "<$dir/$readFile")) {
1713
 
 
1714
 
        if (open(WREP, ">$eventRep")) {
1715
 
 
1716
 
#                   print WREP "Page $numPages\n";
1717
 
            $numPages++;
1718
 
 
1719
 
            while (<RREP>) {
1720
 
 
1721
 
                next if (/Page/);
1722
 
                next if /^#/;
1723
 
 
1724
 
                print WREP "$_";
1725
 
 
1726
 
                if (($numRecords % $numEvents) == 0 && $skip == 0) {
1727
 
                    print WREP "Page $numPages\n";
1728
 
                    $numPages++;
1729
 
                    $skip = 1;
1730
 
                } else {
1731
 
                    $numRecords++;
1732
 
                    $skip = 0;
1733
 
                }
1734
 
 
1735
 
            }
1736
 
            close WREP;
1737
 
        } else {
1738
 
            $error = "Problem in prepSingleLog() - couldn't open $eventRep.";
1739
 
            return $error;
1740
 
        }
1741
 
 
1742
 
        close RREP;
1743
 
 
1744
 
    } else {
1745
 
        $error = "Problem in prepSingleLog() - couldn't open -$dir/$readFile-.";
1746
 
        return $error;
1747
 
    }
1748
 
 
1749
 
    return $error;
1750
 
}
1751
 
 
1752
 
# Cats files in specified directory for easier parsing
1753
 
sub prepArchivedLogs {
1754
 
    my $args = shift;
1755
 
 
1756
 
    my $dir      = '/var/log/apparmor/reports-archived';
1757
 
    my $error    = "0";
1758
 
    my @errors   = ();                                            # For non-fatal errors
1759
 
    my @repList  = ();
1760
 
    my @db       = ();
1761
 
    my $eventRep = "/var/log/apparmor/reports/all-reports.rpt";
1762
 
 
1763
 
    my $useFilters = 0;
1764
 
 
1765
 
    if ($args->{'logFile'}) {
1766
 
        $eventRep = $args->{'logFile'};
1767
 
    }
1768
 
 
1769
 
    if ($args->{'repPath'}) {
1770
 
        $dir = $args->{'repPath'};
1771
 
    }
1772
 
 
1773
 
    # Check to see if we need to use filters
1774
 
    if ($args->{'mode'}
1775
 
        && ($args->{'mode'} =~ /All/ || $args->{'mode'} =~ /^\s*-\s*$/))
1776
 
    {
1777
 
        delete($args->{'mode'});
1778
 
    }
1779
 
    if ($args->{'sdmode'}
1780
 
        && ($args->{'sdmode'} =~ /All/ || $args->{'sdmode'} =~ /^\s*-\s*$/))
1781
 
    {
1782
 
        delete($args->{'sdmode'});
1783
 
    }
1784
 
    if ($args->{'resource'}
1785
 
        && ($args->{'resource'} =~ /All/ || $args->{'resource'} =~ /^\s*-\s*$/))
1786
 
    {
1787
 
        delete($args->{'resource'});
1788
 
    }
1789
 
    if ($args->{'sevLevel'}
1790
 
        && ($args->{'sevLevel'} =~ /All/ || $args->{'sevLevel'} =~ /^\s*-\s*$/))
1791
 
    {
1792
 
        delete($args->{'sevLevel'});
1793
 
    }
1794
 
 
1795
 
    if (   $args->{'prog'}
1796
 
        || $args->{'profile'}
1797
 
        || $args->{'pid'}
1798
 
        || $args->{'denyRes'}
1799
 
        || $args->{'mode'}
1800
 
        || $args->{'sdmode'}
1801
 
        || ($args->{'startdate'} && $args->{'enddate'}))
1802
 
    {
1803
 
 
1804
 
        $useFilters = 1;
1805
 
    }
1806
 
    ############################################################
1807
 
 
1808
 
    # Get list of files in archived report directory
1809
 
    if (opendir(RDIR, $dir)) {
1810
 
 
1811
 
        my @firstPass = grep(/csv/, readdir(RDIR));
1812
 
        @repList =
1813
 
          grep(!/Applications.Audit|Executive.Security.Summary/, @firstPass);
1814
 
        close RDIR;
1815
 
 
1816
 
    } else {
1817
 
        $error = "Failure in prepArchivedLogs() - couldn't open $dir.";
1818
 
        return ($error);    # debug - exit instead?
1819
 
    }
1820
 
 
1821
 
    my @rawDb      = ();
1822
 
    my $numPages   = 1;
1823
 
    my $numRecords = 1;
1824
 
 
1825
 
    # Open record compilation file
1826
 
    if (open(AREP, ">$eventRep")) {
1827
 
 
1828
 
        for (@repList) {
1829
 
 
1830
 
            my $file = $_;
1831
 
 
1832
 
            # Cycle through each $file in $dir
1833
 
            if (open(RPT, "<$dir/$file")) {
1834
 
                push(@rawDb, <RPT>);
1835
 
                close RPT;
1836
 
            } else {
1837
 
                $error = "Problem in prepArchivedLogs() - couldn't open $dir/$file.";
1838
 
                push(@errors, $error);
1839
 
            }
1840
 
        }
1841
 
 
1842
 
        # sort & store cat'd files
1843
 
        if (@rawDb > 0) {
1844
 
 
1845
 
            # Run Filters
1846
 
            if ($useFilters == 1) {
1847
 
 
1848
 
                my @tmpDb = parseMultiDb($args, @rawDb);
1849
 
                @db = sort(@tmpDb);
1850
 
 
1851
 
            } else {
1852
 
                @db = sort(@rawDb);
1853
 
            }
1854
 
 
1855
 
            my $skip = 0;
1856
 
            print AREP "Page $numPages\n";
1857
 
            $numPages++;
1858
 
 
1859
 
            for (@db) {
1860
 
 
1861
 
                next if /^Page/;
1862
 
                next if /^#/;
1863
 
 
1864
 
                print AREP "$_";
1865
 
 
1866
 
                if (($numRecords % $numEvents) == 0 && $skip == 0) {
1867
 
                    print AREP "Page $numPages\n";
1868
 
                    $numPages++;
1869
 
                    $skip = 1;
1870
 
                } else {
1871
 
                    $numRecords++;
1872
 
                    $skip = 0;
1873
 
                }
1874
 
            }
1875
 
 
1876
 
        } else {
1877
 
            $error = "DB created from $dir is empty.";
1878
 
        }
1879
 
 
1880
 
        close AREP;
1881
 
 
1882
 
    } else {
1883
 
        $error = "Problem in prepArchivedLogs() - couldn't open $eventRep.";
1884
 
        push(@errors, $error);
1885
 
    }
1886
 
 
1887
 
    return $error;
1888
 
}
1889
 
 
1890
 
# Similar to parseLog(), but expects @db to be passed
1891
 
sub parseMultiDb {
1892
 
    my ($args, @db) = @_;
1893
 
 
1894
 
    my @newDb = ();
1895
 
 
1896
 
    my $error     = undef;
1897
 
    my $startDate = undef;
1898
 
    my $endDate   = undef;
1899
 
 
1900
 
    # deref dates for speed
1901
 
    if ($args->{'startdate'} && $args->{'enddate'}) {
1902
 
        $startDate = getEpochFromNum("$args->{'startdate'}", 'start');
1903
 
        $endDate   = getEpochFromNum("$args->{'enddate'}",   'end');
1904
 
    }
1905
 
 
1906
 
    $args = rewriteModes($args);
1907
 
 
1908
 
    for (@db) {
1909
 
 
1910
 
        my $rec  = undef;
1911
 
        my $line = $_;
1912
 
 
1913
 
        next if /true|false/;    # avoid horrible yast bug
1914
 
        next if /^Page/;
1915
 
        next if /^#/;
1916
 
        chomp;
1917
 
        next if (!$_ || $_ eq "");
1918
 
 
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'}/; }
1922
 
 
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(/\,/, $_);
1925
 
 
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'};
1931
 
        }
1932
 
 
1933
 
        # Check filters
1934
 
        if ($args->{'pid'} && $args->{'pid'} ne '-') {
1935
 
            next unless ($args->{'pid'} eq $rec->{'pid'});
1936
 
        }
1937
 
        if (   $args->{'sevLevel'}
1938
 
            && $args->{'sevLevel'} ne "00"
1939
 
            && $args->{'sevLevel'} ne '-')
1940
 
        {
1941
 
            if ($args->{'sevLevel'} eq "U") { $args->{'sevLevel'} = '-1'; }
1942
 
            next unless ($args->{'sevLevel'} eq $rec->{'sevLevel'});
1943
 
        }
1944
 
        if ($args->{'mode'} && $args->{'mode'} ne '-') {
1945
 
            next unless ($args->{'mode'} eq $rec->{'mode'});
1946
 
        }
1947
 
 
1948
 
        if ($args->{'denyRes'} && $args->{'denyRes'} ne '-') {
1949
 
            next unless ($args->{'denyRes'} eq $rec->{'denyRes'});
1950
 
        }
1951
 
        if ($args->{'sdmode'} && $args->{'sdmode'} ne '-') {
1952
 
 
1953
 
            # Needs reversal of comparison for sdmode
1954
 
            next unless ($rec->{'sdmode'} =~ /$args->{'sdmode'}/);
1955
 
        }
1956
 
 
1957
 
        push(@newDb, $line);
1958
 
 
1959
 
    }
1960
 
 
1961
 
    return @newDb;
1962
 
}
1963
 
 
1964
 
# Grab & filter events from archived reports (.csv files)
1965
 
sub parseLog {
1966
 
    my $args = shift;
1967
 
 
1968
 
    my @db       = ();
1969
 
    my $eventRep = "/var/log/apparmor/reports/events.rpt";
1970
 
 
1971
 
    if ($args->{'logFile'}) {
1972
 
        $eventRep = $args->{'logFile'};
1973
 
    }
1974
 
 
1975
 
    #my $id = keys(%$db);
1976
 
    #my $rec = undef;
1977
 
    my $error     = undef;
1978
 
    my $startDate = undef;
1979
 
    my $endDate   = undef;
1980
 
 
1981
 
    # deref dates for speed
1982
 
    if ($args->{'startdate'} && $args->{'enddate'}) {
1983
 
        $startDate = getEpochFromNum("$args->{'startdate'}", 'start');
1984
 
        $endDate   = getEpochFromNum("$args->{'enddate'}",   'end');
1985
 
    }
1986
 
 
1987
 
#if ( $args->{'mode'} && ( $args->{'mode'} =~ /All/ || $args->{'mode'} =~ /\s*\-\s*/) ) {}
1988
 
    if ($args->{'mode'}
1989
 
        && ($args->{'mode'} =~ /All/ || $args->{'mode'} =~ /^\s*-\s*$/))
1990
 
    {
1991
 
        delete($args->{'mode'});
1992
 
    }
1993
 
    if ($args->{'sdmode'}
1994
 
        && ($args->{'sdmode'} =~ /All/ || $args->{'sdmode'} =~ /^\s*-\s*$/))
1995
 
    {
1996
 
        delete($args->{'sdmode'});
1997
 
    }
1998
 
    if ($args->{'resource'}
1999
 
        && ($args->{'resource'} =~ /All/ || $args->{'resource'} =~ /^\s*-\s*$/))
2000
 
    {
2001
 
        delete($args->{'resource'});
2002
 
    }
2003
 
    if ($args->{'sevLevel'}
2004
 
        && ($args->{'sevLevel'} =~ /All/ || $args->{'sevLevel'} =~ /^\s*-\s*$/))
2005
 
    {
2006
 
        delete($args->{'sevLevel'});
2007
 
    }
2008
 
 
2009
 
    $args = rewriteModes($args);
2010
 
 
2011
 
    if (open(LOG, "<$eventRep")) {
2012
 
 
2013
 
        # Log Parsing
2014
 
        while (<LOG>) {
2015
 
 
2016
 
            my $rec = undef;
2017
 
 
2018
 
            next if /true|false/;    # avoid horrible yast bug
2019
 
            next if /Page/;
2020
 
            next if /^#/;
2021
 
            chomp;
2022
 
            next if (!$_ || $_ eq "");
2023
 
 
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'}/; }
2027
 
 
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(/\,/, $_);
2030
 
 
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'};
2036
 
            }
2037
 
 
2038
 
            # Check filters
2039
 
            if ($args->{'pid'} && $args->{'pid'} ne '-') {
2040
 
                next unless ($args->{'pid'} eq $rec->{'pid'});
2041
 
            }
2042
 
            if (   $args->{'sevLevel'}
2043
 
                && $args->{'sevLevel'} ne "00"
2044
 
                && $args->{'sevLevel'} ne '-')
2045
 
            {
2046
 
                next unless ($args->{'sevLevel'} eq $rec->{'sevLevel'});
2047
 
            }
2048
 
            if ($args->{'mode'} && $args->{'mode'} ne '-') {
2049
 
                next unless ($args->{'mode'} eq $rec->{'mode'});
2050
 
            }
2051
 
            if ($args->{'denyRes'} && $args->{'denyRes'} ne '-') {
2052
 
                next unless ($args->{'denyRes'} eq $rec->{'denyRes'});
2053
 
            }
2054
 
            if ($args->{'sdmode'} && $args->{'sdmode'} ne '-') {
2055
 
 
2056
 
                # Needs reversal of comparison for sdmode
2057
 
                next unless ($rec->{'sdmode'} =~ /$args->{'sdmode'}/);
2058
 
            }
2059
 
 
2060
 
            push(@db, $rec);
2061
 
 
2062
 
        }
2063
 
 
2064
 
        close LOG;
2065
 
 
2066
 
        # Export results to file if requested
2067
 
        if ($args->{'exporttext'} || $args->{'exporthtml'}) {
2068
 
 
2069
 
            my $rawLog = undef;
2070
 
            my $expLog = undef;
2071
 
 
2072
 
            if ($args->{'exportPath'}) {
2073
 
                $rawLog = $args->{'exportPath'} . '/export-log';
2074
 
            } else {
2075
 
                $rawLog = '/var/log/apparmor/reports-exported/export-log';
2076
 
            }
2077
 
 
2078
 
            if ($args->{'exporttext'} && $args->{'exporttext'} eq 'true') {
2079
 
                $expLog = "$rawLog.csv";
2080
 
                exportLog($expLog, \@db);    # redo w/ @$db instead of %db?
2081
 
            }
2082
 
 
2083
 
            if ($args->{'exporthtml'} && $args->{'exporthtml'} eq 'true') {
2084
 
                $expLog = "$rawLog.html";
2085
 
                exportLog($expLog, \@db);    # redo w/ @$db instead of %db?
2086
 
            }
2087
 
        }
2088
 
 
2089
 
        # write out files to single sorted file (for state, and to speed up yast)
2090
 
        #if (! $args->{'single'} ) {
2091
 
        #       $error = writeEventReport(\@db, $args);
2092
 
        #}
2093
 
 
2094
 
        # changed 04-13-05 - should probably do this, regardless
2095
 
        $error = writeEventReport(\@db, $args);
2096
 
 
2097
 
    } else {
2098
 
        $error = "Couldn't open $eventRep.";
2099
 
    }
2100
 
 
2101
 
    return $error;
2102
 
}
2103
 
 
2104
 
# OLD STUFF -- delete
2105
 
 
2106
 
# deprecated -- replaced by better SQL queries
2107
 
sub OLDgetEssStats {
2108
 
    my $args = shift;
2109
 
 
2110
 
    my $prevTime  = '0';
2111
 
    my $prevDate  = '0';
2112
 
    my $startDate = '1104566401';    # Jan 1, 2005
2113
 
    my $endDate   = time;
2114
 
 
2115
 
    if ($args->{'startdate'}) { $startDate = $args->{'startdate'}; }
2116
 
    if ($args->{'enddate'})   { $endDate   = $args->{'enddate'}; }
2117
 
 
2118
 
    my $query = "SELECT * FROM events";
2119
 
 
2120
 
    # hostIp, startDate, endDate, sevHi,  sevMean, numRejects
2121
 
    my $eventDb = getEvents($query, "", "$startDate", "$endDate");
2122
 
 
2123
 
    my @hostIdx = ();                # Simple index to all hosts for quick host matching
2124
 
    my @hostDb  = ();                # Host-keyed data for doing REJECT stats
2125
 
 
2126
 
    # Outer Loop for Raw Event db
2127
 
    for (@$eventDb) {
2128
 
 
2129
 
        my $ev = $_;                 # current event record
2130
 
 
2131
 
        if ($ev->{'host'}) {
2132
 
 
2133
 
            # Create new host entry, or add to existing
2134
 
            if (grep(/$ev->{'host'}/, @hostIdx) == 1) {
2135
 
 
2136
 
                # Inner loop, but the number of hosts should be small
2137
 
                for my $hdb (@hostDb) {
2138
 
 
2139
 
                    if ($hdb->{'host'} eq $ev->{'host'}) {
2140
 
 
2141
 
                        if ($hdb->{'startdate'} gt $ev->{'date'}) {
2142
 
                            $hdb->{'startdate'} = $ev->{'date'};    # Find earliest start date
2143
 
                        }
2144
 
 
2145
 
                        $hdb->{'numEvents'}++;                      # tally all events reported for host
2146
 
 
2147
 
                        if ($ev->{'sdmode'}) {
2148
 
                            if ($ev->{'sdmode'} =~ /PERMIT/) {
2149
 
                                $hdb->{'numPermits'}++;
2150
 
                            }
2151
 
                            if ($ev->{'sdmode'} =~ /REJECT/) {
2152
 
                                $hdb->{'numRejects'}++;
2153
 
                            }
2154
 
                            if ($ev->{'sdmode'} =~ /AUDIT/) {
2155
 
                                $hdb->{'numAudits'}++;
2156
 
                            }
2157
 
                        }
2158
 
 
2159
 
                        # Add stats to host entry
2160
 
                        #if ( $ev->{'severity'} && $ev->{'severity'} =~ /\b\d+\b/ ) {}
2161
 
                        if ($ev->{'severity'} && $ev->{'severity'} != -1) {
2162
 
 
2163
 
                            $hdb->{'sevNum'}++;
2164
 
                            $hdb->{'sevTotal'} = $hdb->{'sevTotal'} + $ev->{'severity'};
2165
 
 
2166
 
                            if ($ev->{'severity'} > $hdb->{'sevHi'}) {
2167
 
                                $hdb->{'sevHi'} = $ev->{'severity'};
2168
 
                            }
2169
 
                        } else {
2170
 
                            $hdb->{'unknown'}++;
2171
 
                        }
2172
 
                    }
2173
 
                }
2174
 
 
2175
 
            } else {
2176
 
 
2177
 
                # New host
2178
 
                my $rec = undef;
2179
 
                push(@hostIdx, $ev->{'host'});    # Add host entry to index
2180
 
 
2181
 
                $rec->{'host'}      = $ev->{'host'};
2182
 
                $rec->{'startdate'} = $startDate;
2183
 
 
2184
 
                #$rec->{'startdate'} = $ev->{'date'};
2185
 
 
2186
 
                if ($endDate) {
2187
 
                    $rec->{'enddate'} = $endDate;
2188
 
                } else {
2189
 
                    $rec->{'enddate'} = time;
2190
 
                }
2191
 
 
2192
 
                # Add stats to host entry
2193
 
                if ($ev->{'sev'} && $ev->{'sev'} ne "U") {
2194
 
 
2195
 
                    $rec->{'sevHi'}    = $ev->{'sev'};
2196
 
                    $rec->{'sevTotal'} = $ev->{'sev'};
2197
 
                    $rec->{'sevNum'}   = 1;
2198
 
                    $rec->{'unknown'}  = 0;
2199
 
 
2200
 
                } else {
2201
 
                    $rec->{'sevHi'}    = 0;
2202
 
                    $rec->{'sevTotal'} = 0;
2203
 
                    $rec->{'sevNum'}   = 0;
2204
 
                    $rec->{'unknown'}  = 1;
2205
 
                }
2206
 
 
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
2212
 
 
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'}++; }
2217
 
                }
2218
 
 
2219
 
                push(@hostDb, $rec);         # Add new records to host data list
2220
 
            }
2221
 
 
2222
 
        } else {
2223
 
            next;                            # Missing host info -- big problem
2224
 
        }
2225
 
    }    # END @eventDb loop
2226
 
 
2227
 
    # Process simple REJECT-related stats (for Executive Security Summaries)
2228
 
    for (@hostDb) {
2229
 
 
2230
 
# In the end, we want this info:
2231
 
#   - Hostname, Startdate, Enddate, # Events, # Rejects, Ave. Severity, High Severity
2232
 
 
2233
 
        if ($_->{'sevTotal'} > 0 && $_->{'sevNum'} > 0) {
2234
 
            $_->{'sevMean'} = Immunix::Reports::round($_->{'sevTotal'} / $_->{'sevNum'});
2235
 
        } else {
2236
 
            $_->{'sevMean'} = 0;
2237
 
        }
2238
 
 
2239
 
        # Convert dates
2240
 
        if ($_->{'startdate'} !~ /:/) {
2241
 
            $_->{'startdate'} = Immunix::Reports::getDate($startDate);
2242
 
        }
2243
 
        if ($_->{'enddate'} !~ /:/) {
2244
 
            $_->{'enddate'} = Immunix::Reports::getDate($_->{'enddate'});
2245
 
        }
2246
 
 
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'});
2253
 
 
2254
 
    }
2255
 
 
2256
 
    return (\@hostDb);
2257
 
}
2258
 
 
2259
 
1;
2260