[SCRIPT] avctree
Lee Kok Seng
kokseng at ieee.org
Tue May 15 04:13:50 UTC 2007
Hello,
While diagnosing avc messages, I found the log message too spread-out
to form a mental picture,
since the lack of some rules often result in several domain domains
barking. This reminds me of
unforgiving Ada compilers spilling out loads of messages. I did not
want to use audit2allow
too quickly until I understood what the machine is not happy with.
So, I needed a message
format that let me to that.
Here is a simple perl script to parse log files for avc denial
messages, index, sort them, and print them
in tree (single depth) view, which I hastily put together last
night. I hope it will help others as it did
for me. Please feel free to modify it for your own use.
You can index the message by any key, for example scontext, tcontext,
action, name and etc. You can
also specify the log files to parse. By default, the script trim the
context string of the _u, _r and _t, which
are good for rule readability in source files, but clutter diagnostic
print out. However, if this bothers you,
disable trimming by --trim=no option. To get help and condition of
usage, avctree --help. It is
best to pipe the output to less so that you can navigate.
A typical partial print out is as follows (this one indexed by
tcontext):
#
------------------------------------------------------------------------
--------[tcontext]
|
+-[root-object-default ]
| +<- system-system-initrc_su su(1753) : dir : search : home :
dm-0 : 49182
|
+-[root-object-selinux_config]
| +<- root-system-semanage semodule(3584) : dir : rename :
active : sdb1 : 49833
|
+-[root-object-user_home ]
| +<- root-system-semanage semodule(10359) : lnk_file :
read : targeted : sdb1 : 98758
| +<- root-system-semanage semodule(11006) : lnk_file :
read : policy : sdb1 : 98764
| +<- root-system-semanage semodule(3584) : lnk_file : read :
targeted : sdb1 : 98758
|
+-[system-object-default ]
| +<- system-system-initrc_su su(1753) : dir : search : / :
dm-0 : 2
| +<- system-system-hald hald(1958) : dir : getattr : / :
dm-0 : 2
|
No PP module for this script yet. This script use basic Perl
features, so, as along you have base Perl package installed, it
should work.
Be happy to hear any comments or suggestion for improving this.
/ks
#------------------------------------------- cut here
-----------------------------------------
#!/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
}
my $version='1.0.0';
use strict;
use warnings;
my $thisScript = $0;
$thisScript =~ s#([^/]+[/]|)(\w+)#$2#;
#
------------------------------------------------------------------------
----------------------
use Getopt::Long;
sub usage
{
lmsg;
print "Usage:\n";
print <<USAGETXT;
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
--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.
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
usage(), exit unless GetOptions(
'log:s' => \$logARG,
'tags!' => \$tagsARG,
'key:s' => \$catARG,
'trim:s' => \$trimARG,
'help!' => \$helpARG
);
usage() if (defined($helpARG));
##
------------------------------------------------------------------------
----------------------
## Option: skip tags
my $skiptags = defined($tagsARG)?0:1;
## Option: log files
my @logOPT = grep -d $_, split /,|\n|\r/, $logARG if (defined($logARG));
@logOPT = ('/var/log/messages','/var/log/kernel','/var/log/debug')
if (defined($logARG) && ((!scalar @logOPT) || grep /all/, @logOPT));
@logOPT = ('/var/log/kernel') if (!scalar @logOPT && -d '/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;
##
------------------------------------------------------------------------
----------------------
#
## 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+for\s+(.*)/;
## Holds indexed avc message records
my %avc;
##
------------------------------------------------------------------------
----------------------
## 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;
}
##
------------------------------------------------------------------------
----------------------
## readLOG log-file-name
# Reads the specified log file
sub readLOG
{
my $avc = shift;
my $logfile = 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;
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;
}
next if (!defined($this{'scontext'}) || !defined($this{'tcontext'}));
$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'}));
# Okay, let's index the records with various keys
foreach (keys %this) {
next if (/audit|timetag|file/);
$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|$kcat/);
next if ($skiptags && $kmsg =~ /timetag|audit/);
$buf .= sprintf "%s=%s ", $kmsg, $hmsg->{$kmsg};
}
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
readLOG(\%avc, $_) foreach (@logOPT);
# Decide which category to print
@catOPT = (sort keys %avc) if (defined($catARG) && (! scalar
@catOPT) || grep /all/, at catOPT ) ;
@catOPT = @catDEF if (!defined($catARG));
print "\n> 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\n";
keyTREE(\%avc, $_,scalar @logOPT > 1?1:undef) foreach (@catOPT);
##
------------------------------------------------------------------------
----------------------
# vim :ts=4:sw=4:
1;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listman.redhat.com/archives/fedora-selinux-list/attachments/20070515/d508a832/attachment.htm>
More information about the fedora-selinux-list
mailing list