
##########################################################################
# $Id: audit,v 1.4 2005/10/26 05:52:12 bjorn Exp $
##########################################################################
# $Log: audit,v $
# Revision 1.4  2005/10/26 05:52:12  bjorn
# Filtering modifications by Ivana Varekova
#
# Revision 1.3  2005/10/01 19:09:07  bjorn
# Extensive modifications by Ivana Varekova
#
# Revision 1.2  2005/06/07 18:43:32  bjorn
# Limiting number of unmatched statement
#
# Revision 1.1  2005/05/04 15:56:13  bjorn
# Audit (for selinux) submitted by Ron Kuris
#
##########################################################################
########################################################
# selinux audit log summaries
#
# This was written and is maintained by:
#    Ron Kuris <swcafe@gmail.com>
#
# Please send all comments, suggestions, bug reports,
#    etc, to logwatch-devel@logwatch.org
########################################################
use strict;
use Logwatch ':all';

my (%denials, %grants);
my @OtherList;
my $othercount = 0;
my $Debug = ($ENV{'LOGWATCH_DEBUG'} || 0);
my $Detail = ($ENV{'LOGWATCH_DETAIL_LEVEL'} || 0);
my $NumberOfInits = 0;
my $NumberOfDStarts = 0;
my $NumberOfDStartsPid = 0;
my $NumberOfDStops = 0;
my $NumberOfDdStarts = 0;
my $NumberOfDdStops = 0;
my $NumberOfLostMessages = 0;
my %InvalidContext = ();
my %BugLog = ();
my $UELimit = 10;
my $ThisLine; 

# No sense in running if selinux doesn't even exist on this system
exit(0) unless -d '/selinux';

print STDERR "\n\nDEBUG: Inside audit filter\n\n" if ( $Debug >= 5 );

while ($ThisLine = <STDIN>) {
    chomp($ThisLine);
    if (( $ThisLine =~ /initializing netlink socket \(disabled\)/) or 
	( $ThisLine =~ /audit_pid=[0-9]* old=[0-9]*(?: by auid=[0-9]*)?/) or
	( $ThisLine =~ /(arch=[0-9]+ )?syscall=[0-9]+ (success=(no|yes) )?exit=[0-9-]+( a[0-3]=[0-9a-f]+)* items=[0-9]+ pid=[0-9]+ (loginuid=[0-9-]+ )?(auid=[0-9]+ )?uid=[0-9]+ gid=[0-9]+ euid=[0-9]+ suid=[0-9]+ fsuid=[0-9]+ egid=[0-9]+ sgid=[0-9]+ fsgid=[0-9]+/) or
	( $ThisLine =~ /Audit daemon rotating log files/) or
	( $ThisLine =~ /audit_backlog_limit=[0-9]* old=[0-9]*(?: by auid=[0-9]*)?/) or
	( $ThisLine =~ /SELinux:  unrecognized netlink message type=[0-9]+ for sclass=[0-9]+/) or
	( $ThisLine =~ /audit\([0-9.]+:[0-9]+\): saddr=[0-9]+/) or
	( $ThisLine =~ /nargs=[0-9]+ a0=[0-9a-f]+ a1=[0-9a-f]+ a2=[0-9a-f]+ a3=[0-9a-f]+ a4=[0-9a-f]+ a5=[0-9a-f]+/)
    ) { 
	# Ignore these entries
    } elsif ( $ThisLine =~ /audit\([0-9]{10}.[0-9]{3}:[0-9]\): initialized$/) {
      $NumberOfInits++;
    } elsif ( $ThisLine =~ /Init complete, audit pid set to: [0-9]+/) {
      $NumberOfDStartsPid++;
    } elsif ( $ThisLine =~ /Init complete, auditd [0-9,.]+ listening for events/) {
      $NumberOfDStarts++; 
    } elsif ( $ThisLine =~ /The audit daemon is exiting./) {
      $NumberOfDStops++;
    } elsif ( $ThisLine =~ /audit_lost=[0-9]+ audit_backlog=[0-9]+ audit_rate_limit=[0-9]+ audit_backlog_limit=[0-9]+$/) {
      $NumberOfLostMessages++;
    } elsif ( $ThisLine =~ /auditd startup succeeded/) {
      $NumberOfDdStarts++;
    } elsif ( $ThisLine =~ /auditd shutdown succeeded/) {
      $NumberOfDdStops++;
    } elsif (( $ThisLine =~ /netlink socket too busy/) or 
             ( $ThisLine =~ /Error sending signal_info request \(Invalid argument\)/) or 
	     ( $ThisLine =~ /major=[0-9]+ name_count=[0-9]+: freeing multiple contexts \([1-2]\)/)) {
      $ThisLine =~ s/audit\(:[0-9]+\): //;
      $BugLog{$ThisLine}++;
    } elsif ( $Detail > 9 ) {
	if ( $ThisLine =~ /avc:\s*denied\s*{\s*([^}]+).*scontext=(\S+)\s*tcontext=(\S+)\s*tclass=(\S+)/ ) {
	    $denials{$2.' '.$3.' ('.$1.$4 . ')'}++;
	} elsif ( $ThisLine =~ /avc:\s*granted\s*{\s*([^}]+).*scontext=(\S+)\s*tcontext=(\S+)\s*tclass=(\S+)/ ) {
	    $grants{$2.' '.$3.' ('.$1.$4 . ')'}++;
	} elsif ($ThisLine =~ /security_compute_sid:\s*invalid context\s*(\S+)\s*for\s*scontext=(\S+)\s*tcontext=(\S+)\s*tclass=(\S+)/ ) {
	    $InvalidContext{$4." running as ".$2." acting on ".$3." \nshould transit to invalid ".$1}++;
	} else { 
            $othercount++;
            $ThisLine =~ s/^\s*//;
            if ($othercount < $UELimit+1) {
	       push @OtherList, $ThisLine;
            }
	}
    } elsif ( $Detail > 4 ) {
	if ( $ThisLine =~ /avc:\s*denied\s*{\s*[^}]+.*scontext=(\S+)\s*tcontext=(\S+)\s*tclass=(\S+)/ ) {
	    $denials{$1.' '.$2.' ('.$3 . ')'}++;
	} elsif ( $ThisLine =~ /avc:\s*granted\s*{\s*[^}]+}.*scontext=(\S+)\s*tcontext=(\S+)\s*tclass=(\S+)/ ) {
	    $grants{$1.' '.$2.' ('.$3 . ')'}++;
	} elsif ($ThisLine =~ /security_compute_sid:\s*invalid context\s*(\S+)\s*for\s*scontext=(\S+)\s*tcontext=\S+\s*tclass=(\S+)/ ) {
	    $InvalidContext{$3." running as ".$2." should transit to invalid ".$1}++;
	} else {
            $othercount++;
            $ThisLine =~ s/^\s*//;
            if ($othercount < $UELimit+1) {
	       push @OtherList, $ThisLine;
            }
	}
    } else {
	if ( $ThisLine =~ /avc:\s*denied\s*{\s*[^}]+.*scontext=([^:]+):[^:]+:\S+\s*tcontext=([^:]+):[^:]+:\S+\s*tclass=(\S+)/ ) {
	    $denials{$1.' '.$2.' ('.$3 . ')'}++;
	} elsif ( $ThisLine =~ /avc:\s*granted\s*{\s*[^}]+.*scontext=([^:]+):[^:]+:\S+\s*tcontext=([^:]+):[^:]+:\S+\s*tclass=(\S+)/ ) {
	    $grants{$1.' '.$2.' ('.$3 . ')'}++;
	} elsif ($ThisLine =~ /security_compute_sid:\s*invalid context\s*(\S+)\s*for\s*scontext=(\S+)\s*tcontext=\S+\s*tclass=(\S+)/ ) {
   	    $InvalidContext{$3." running as ".$2." should transit to invalid ".$1}++;  
	} else {
            $othercount++;
            $ThisLine =~ s/^\s*//;
            if ($othercount < $UELimit+1) {
	       push @OtherList, $ThisLine;
            }
	}
    }
}

if ( keys %denials ) {
    print "\n\n*** Denials ***\n";
    foreach my $key (sort keys %denials) {
    	print "   $key: ". $denials{$key} . " times\n";
    }
}

if ( keys %grants ) {
    print "\n\n*** Grants ***\n";
    foreach my $key (sort keys %grants) {
    	print "   $key: ". $grants{$key} . " times\n";
    }
}

if ( keys %InvalidContext) {
    print "\n\n*** Invalid Context ***\n";
    foreach my $key (sort keys %InvalidContext) {
        print "   $key: ". $InvalidContext{$key} . " times\n";
    }
}
		    


if ($NumberOfDStarts+$NumberOfDStartsPid) {
    print "\n Number of audit daemon starts: ",$NumberOfDStarts+$NumberOfDStartsPid," \n";
}

if (($Detail >9) and ($NumberOfDStartsPid)) {
    print "        starts with pid change: $NumberOfDStartsPid \n"
}

if ($NumberOfDStops)  {
    print "\n Number of audit daemon stops: $NumberOfDStops \n";
}

if ($NumberOfLostMessages) {
    print "\n Number of lost messages: $NumberOfLostMessages\n";
}

if ($Detail>9) {
    if ($NumberOfInits) {
        print "\n Number of audit initializations: $NumberOfInits \n";
    }
    if ($NumberOfDdStarts) {
      print "\n Number of auditd daemon starts: $NumberOfDdStarts \n";
    }
    if ($NumberOfDdStops) {
      print "\n Number of auditd daemon stops: $NumberOfDdStops \n";
    }
}
    
if ( %BugLog) {
  print "\n*** Logs which could mean a bug ***\n";
  foreach my $Entry (keys %BugLog) {
    print "   $Entry\n";
  }
}

if ( $#OtherList >= 0 ) {
    print "\n**Unmatched Entries** ";
    if ($othercount > $UELimit) {
       print "(Only first $UELimit out of $othercount are printed)";
    }
    print "\n ";
    print join("\n ", @OtherList);
    print "\n";
}

exit(0);
