[SCRIPT] avctree 1.0.4

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Fedora Users]     [Fedora Desktop]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux