Hello,
Here is version 1.0.4 of the script previously posted.
a. Added regular expression (perl) to select messages to display
e.g avctree --re="context=~/java/" will show any avc message
that has 'java' in
scontext *or* tcontext.
e.g avctree --re="*=~/initrc/" will show any avc messages that
has 'su' anywhere.
b. Added message selection based on age of message
e.g avctree --age 3h will show avc messages not older than 3
hours from when you run the script.
c. Added 'unique' format of print
e.g avctree --uniq will show avc messages that are unique once,
i.e. scontext, tcontext, comm,
name, dev, ino, key all match up (except time tag, audit tag,
pid ... so, use with this in mind)
Try this: avctree --uniq --age 1d
/ks
p/s: This post may have duplicates resulting in problem in posting
this update. Sorry if so.
This will also be 'it' for the time being, having got this
script to give me the productivity
I need to work with selinux.
------------------------------------------------------ cut
---------------------------------------------
#!/usr/bin/perl -w
sub lmsg
{
print <<LMSG;;
# Copyright (C) 2007, LEE, "Kok Seng" (kokseng at ieee dot org)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# 02111-1307 USA
#
LMSG
}
# History
# 1.0.0 Format avc messages
# 1.0.1 added --re option to select messages to print
# 1.0.2 --re option allow context to mean scontext or tcontext, all
to mean any key
# 1.0.3 added --age option to select based on age of message
# 1.0.4 added --uniq option to show messages that are unique
my $version='1.0.4+';
use strict;
use warnings;
my ($thisScript) = ($0 =~ /.*?\/*(\w+)$/);
#
------------------------------------------------------------------------
----------------------
use Getopt::Long;
sub usage
{
lmsg;
print "Usage:\n";
print <<USAGETXT;
$thisScript version $version
Utility to format avc messages for readability
-----------------------------------------------------------------------
---------------------
$thisScript [[options] ...]
Options:
--log=[ all | file,...] : List log files to parse, delimited
by comma.
no argument or all => /var/log/
messages, /var/log/kernel
--tags : Show time and audit tags
--key=key,... : List messages indexed-sorted by
specified key
no argument or all => all keys
Not specified =>
scontext,tcontext,action,comm,name
--trim=yes|no|1|0 : Trim context string. Default: yes
--re="expr" : Filter using rsupplied regular
expression
special:
a. To match either scontext or
tcontext, specify
--re="context=~/^(dhcp|su)/"
which will match scontext
or tcontext if either starts
wuth dhcp or su.
b. To match any key's value, specify
--re="*=~/dhcp|su/" which will
print all messages
that has dhcp or su in any
key's value
--age[=time-spec] : Show messages that are not older
than specified age.
time spec := numeric[unit]
numeric := integer or
float default: 10
unit := s | m | h | d |
w default: m
--uniq : Show in unique format.
--help
-----------------------------------------------------------------------
---------------------
Examples:
a. $thisScript --key=scontext
Print avc messages indexed-sorted by source context.
b. $thisScript --key=tcontext
Print avc messages indexed-sorted by target context.
c. $thisScript --key=comm
Print avc messages indexed-sorted by command executed.
d. $thisScript --key=name
Print avc messages indexed-sorted by target object's name.
e. $thisScript --key=all or $thisScript --key
Print avc messages indexed-sorted by all keys.
f. $thisScript
Print avc messages indexed-sorted by scontext, tcontext,
comm, name (default)
g. $thisScript --trim=no
Print avc messages without trimming context string.
h. $thisScript --log=/var/log/messages,/var/log/messages.1,/var/
log/messages.2
Print avc messages from log files listed (delimited by
comma).
i. $thisScript --tags
Print avc messages, including in each message the log time
tag and audit tag.
j. $thisScript --re="name=~/dhcp/ && comm=~/dhcp/"
Print avc messages which has dhcp in name or comm
USAGETXT
exit -1;
}
#
my $logARG; # Log files to parse
my $tagsARG; # Show time and audit tags
my $catARG; # Categories to print
my $helpARG; # Help
my $trimARG; # Trim context string for readability
my $ageARG; # Age specification
my $reARG; # Regular Expression
my $uniqARG; # Unique format
usage(), exit unless GetOptions(
'log:s' => \$logARG,
'tags!' => \$tagsARG,
'key:s' => \$catARG,
'trim:s' => \$trimARG,
're:s' => \$reARG,
'age:s' => \$ageARG,
'uniq!' => \$uniqARG,
'help!' => \$helpARG
);
usage() if (defined($helpARG));
##
------------------------------------------------------------------------
----------------------
## Option: skip tags
my $skiptags = defined($tagsARG)?0:1;
## Option: log files
my @logOPT = grep -e $_, split /,|\n|\r/, $logARG if (defined
($logARG));
@logOPT = ('/var/log/messages','/var/log/kernel','/var/log/debug','/
var//log/audit')
if (defined($logARG) && ((!scalar @logOPT) || grep /all/, @logOPT));
@logOPT = ('/var/log/audit') if (!scalar @logOPT && -e '/var/log/
audit');
@logOPT = ('/var/log/kernel') if (!scalar @logOPT && -e '/var/log/
kernel');
@logOPT = ('/var/log/messages') if (!scalar @logOPT);
## Option: Category
my @catOPT = split /,|\n|\r/, $catARG if (defined($catARG));
my @catDEF = ('scontext','tcontext','comm','name');
## Option: Trim
my $trimOPT = defined($trimARG) ? ($trimARG =~ /no|0|/i ? 0 : 1 ) : 1;
## Option: regular expression
my @reOPT = split /,|\n|\r/, $reARG if (defined($reARG));
## Option: age
my @ageOPT = split /,|\n|\r/, $ageARG if (defined($ageARG));
@ageOPT = ('10m') if (defined($ageARG) && !scalar @ageOPT);
my ($age, $tu) = ($ageOPT[0] =~ /\s*([\d\.]+)\s*([smhdw]).*/);
undef $ageARG if (!defined($age));
$age *= defined($tu)?($tu eq 'm'?60:($tu eq 'h'?3600:($tu eq 'd'?
86400:($tu eq 'w'?604800:1)))):1 if (defined($ageARG));
##
------------------------------------------------------------------------
----------------------
#
## Regular expression for parsing avc's 'denied' messages
my $avcRE = qr/^(\w{3}\s+\d{2}\s+\d{2}:\d{2}:\d{2})[\s\w]+:\s*audit\
(([\d.:]+)\)\s*:\s*avc\s*:\s*denied\s+\{\s+([\w\s]+)\s+}\s+for\s+(.*)/;
## Holds indexed avc message records
my %avc;
my $epoch = time();
##
------------------------------------------------------------------------
----------------------
## contextFMT
# Format context string for readability
sub contextFMT
{
my $ctxt = shift;
my ($u,$r,$t,$l) = split /:/, $ctxt;
$u =~ s/(.*)_./$1/;
$r =~ s/(.*)_./$1/;
$t =~ s/(.*)_./$1/;
return $u . '-' . $r . '-' . $t;
}
##
------------------------------------------------------------------------
----------------------
## prepRE reference-avc reference-re-list
# Prepare regular expression from user supplied string
sub prepRE
{
my $avc = shift;
my $reOPT = shift;
my @re = @$reOPT;
if (grep /\*\s*=~/, @re) {
my @restr;
push @restr, /\s*\*\s*=~\s*\/([^\/]+)\/\s*/g foreach (@re);
my $str = "{my \$ret=0; (\$ret |= (";
s/(.*)/\$this{\$_}=~\/$_\// foreach (@restr);
$str .= join ' || ', @restr;
$str .= ")) foreach (keys \%this); \$ret;}";
#print "\nprepRE=$str";
return $str;
} else {
# tbd : improve on regex / ... / parsing
s/\s*context\s*(=~\s*\/[^\/]+\/)\s*/\(scontext$1||tcontext$1\)/g
foreach (@re);
s/\s*(\w+)\s*=~/\$this\{\'$1\'\} =~/g foreach (@re);
my $str = '(' . join(' ) || ( ' , @re) . ')';
$str =~ s/\|\|\s*\(\s*\)//g;
return $str;
}
}
##
------------------------------------------------------------------------
----------------------
## readLOG log-file-name
# Reads the specified log file
sub readLOG
{
my $avc = shift;
my $logfile = shift;
my $reopt = shift;
my $logsn = ($logfile =~ /.*\/(.*)$/)[0];
my $tmax = defined($avc->{'_tcontext_max_'})?$avc->
{'_tcontext_max_'}:0;
my $smax = defined($avc->{'_scontext_max_'})?$avc->
{'_scontext_max_'}:0;
my $rex = undef;
open LOGF, '<' . $logfile || die "Cannot open input file: $logfile";
while (<LOGF>) {
s/\r|\n//g;
next if (!$_);
next if (!/\s+avc:\s+/);
my ($timetag, $audit, $action, $detail) = ($_ =~ /$avcRE/);
next if (!defined($action)||!defined($detail)||!defined($timetag)||!
defined($audit));
# okay, we have a avc 'denied' message
my %this; # this hash will keep the message's key=value
my @fields = split /\s|\r|\n/, $detail;
foreach (@fields) {
next if (!$_);
my ($key,$val) = split/=/;
next if (!$key||!$val);
$val =~ s/[\"\']*([^\"\']*)[\"\']*/$1/;
$this{"$key"} = $val;
}
# check age of message
if (defined($ageARG) && defined($audit)) {
my ($tm) = ($audit =~ /([\d+\.]+).*/);
next if (defined($tm) && $epoch - $tm > $age);
}
# Filter, if specified
$rex = prepRE($avc, $reopt) if (!defined($rex) && defined($reopt));
next if (defined($rex) && ! eval $rex );
$this{'action'} = $action;
$this{'timetag'} = $timetag;
$this{'audit'} = $audit;
$this{'file'}= $logsn;
if ($trimOPT) {
$this{'scontext'} = contextFMT($this{'scontext'});
$this{'tcontext'} = contextFMT($this{'tcontext'});
}
$smax = length($this{'scontext'}) if ($smax < length($this
{'scontext'}));
$tmax = length($this{'tcontext'}) if ($tmax < length($this
{'tcontext'}));
# Check if this message is unique
my $uniq = 1;
if (defined($uniqARG)&&defined($avc{'scontext'})&&defined($avc
{'scontext'}->{$this{'scontext'}})) {
foreach (@{$avc{'scontext'}->{$this{'scontext'}}}) {
if ($_->{'tcontext'} eq $this{'tcontext'} &&
($_->{'comm'} eq $this{'comm'})&&
($_->{'name'} eq $this{'name'}) &&
($_->{'tclass'} eq $this{'tclass'}) &&
($_->{'action'} eq $this{'action'}) &&
(!defined($_->{'dev'}) || $_->{'dev'} eq $this{'dev'}) &&
(!defined($_->{'ino'}) || $_->{'ino'} eq $this{'ino'}) &&
(!defined($_->{'key'}) || $_->{'key'} eq $this{'key'})
) {
$_->{'_same_'} = [()] if (!defined($_->{'_same_'}));
push @{$_->{'_same_'}}, \%this;
$uniq = 0;
last;
}
}
}
next if (!$uniq);
# Okay, let's index the records with various keys
foreach (keys %this) {
next if (/audit|timetag|file|_same_/);
$avc->{$_} = {} if (!defined($avc->{$_}));
$avc->{$_}->{$this{$_}} = [()] if (!defined($avc->{$_}->{$this
{$_}}));
push @{$avc->{$_}->{$this{$_}}}, \%this;
}
}
close LOGF;
$avc->{'_scontext_max_'} = $smax;
$avc->{'_tcontext_max_'} = $tmax;
}
##
------------------------------------------------------------------------
----------------------
##
# keyTREE key
# Show selected key in a tree view
sub keyTREE
{
my $avc = shift;
my $kcat = shift;
my $showfile = shift;
my $hcat = $avc->{$kcat};
my $lvl = 1;
my $isSctx = ($kcat =~ /scontext/);
my $isTctx = ($kcat =~ /tcontext/);
my $smax = $isSctx ? 0 : $avc->{'_scontext_max_'};
my $tmax = $isTctx ? 0 : $avc->{'_tcontext_max_'};
return if (/_scontext_max_|_tcontext_max_/);
print "\n# "; for ($_=0; $_ < 80; $_++) {print "-";}
print "[", $kcat, "]\n";
foreach my $kmsg (sort keys %$hcat) {
printf "|\n+-[%-*s]\n", $smax, $hcat->{$kmsg}[0]->{$kcat};
$lvl++;
my $buf;
my $i;
my $cnt = scalar @{$hcat->{$kmsg}};
foreach my $hmsg (@{$hcat->{$kmsg}}) {
$buf .= sprintf "%s %-*s %s %-*s %s%s(%s) : %s : %s %s%s%s\n",
$isTctx? '+<-' : '+->',
$smax, $isSctx?'':$hmsg->{'scontext'},
$isTctx||$isSctx ? '' : '-+->',
$tmax, $isTctx?'':$hmsg->{'tcontext'},
defined($showfile)? $hmsg->{'file'}.'> ':'',
$hmsg->{'comm'}, $hmsg->{'pid'}, $hmsg->{'tclass'},
$hmsg->{'action'},
defined($hmsg->{'name'})?': '.$hmsg->{'name'}:'',
defined($hmsg->{'key'})?' : '.$hmsg->{'key'}:'',
defined($hmsg->{'dev'})?' : '.$hmsg->{'dev'} . (defined($hmsg->
{'ino'})?' : '.$hmsg->{'ino'}:'') : ''
;
$i = $lvl; $buf = '| ' . $buf while (--$i); print $buf; $buf = "";
foreach my $kmsg (sort keys %$hmsg) {
next if ($kmsg =~ /file|scontext|tcontext|comm|pid|tclass|action|
name|dev|ino|key|_same_|$kcat/);
next if ($skiptags && $kmsg =~ /timetag|audit/);
$buf .= sprintf "%s=%s ", $kmsg, $hmsg->{$kmsg};
}
$buf .= sprintf "+-[%u] similar message%s", scalar @{$hmsg->
{'_same_'}},
scalar @{$hmsg->{'_same_'}} > 1 ? 's':''
if (defined($uniqARG) && defined($hmsg->{'_same_'}));
if ($buf) {
$buf = sprintf "%*s%s\n", ($cnt==1)?$smax+$tmax+10+2:$smax+$tmax
+10, ,"", $buf;
$i = (--$cnt)? $lvl:$lvl-1; $buf = '| ' . $buf while ($i--);
print $buf; $buf = "";
}
}
$lvl--;
}
$lvl--;
}
##
------------------------------------------------------------------------
----------------------
# Parse log files
my @logLIST=@logOPT;
readLOG(\%avc, $_, scalar @reOPT?\@reOPT:undef) foreach (@logLIST);
# Decide which category to print
@catOPT = (sort keys %avc) if (defined($catARG) && (! scalar
@catOPT) || grep /all/,@catOPT ) ;
@catOPT = @catDEF if (!defined($catARG));
print "\n> $thisScript version $version, Copyright (C) 2007, LEE,
\"Kok Seng\" (kokseng at ieee dot org)";
print "\n> Notice: get help and condition of usage inforamtion
regarding this script: $thisScript --help";
print "\n> File(s) parsed: ", join ', ', @logOPT, " Key(s) : ", join
', ', @catOPT;
print "\n> Regular expression = ", join ' or ', @reOPT if (scalar
@reOPT);
print "\n> Age not more than ", $ageARG, " (", $age, " seconds)" if
(defined($ageARG));
print "\n> Unique mode is ON" if (defined($uniqARG));
print "\n";
keyTREE(\%avc, $_,scalar @logOPT > 1?1:undef) foreach (@catOPT);
##
------------------------------------------------------------------------
----------------------
# vim :ts=4:sw=4:
1;
--
fedora-selinux-list mailing list
fedora-selinux-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/fedora-selinux-list