[SCRIPT] avctree 1.0.4

Lee Kok Seng kokseng at ieee.org
Thu May 17 06:29:58 UTC 2007


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/, at 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;




More information about the fedora-selinux-list mailing list