#!/usr/bin/perl -w

use strict;

my %new_normal_files=();
my %del_normal_files=();
my %change_normal_files=();

my %new_bad_files=();
my %del_bad_files=();
my %info_bad_files=();
my %change_bad_files=();

my %symlinks=();

my $process = 1;

while(<STDIN>) 
{

    my @fields = split /\t+/;
    s/^\s*(\S+.*?)\s*$/$1/ foreach (@fields); #trim

    if (/^Init\s+(.*?)\.\.\.$/)
    {
	print;
	$process = 0;
	next;
    }

    if (/^Processing\s+(.*?)\.\.\.$/)
    {
	print;
	$process = 1;
	next;
    }
    
    my $first_bad  = undef; #first possible bad comment
    my $second_bad = undef; #second possible bad comment

    #always save bad file information if we have it
    if ($fields[3] and $fields[3] =~ m/.*\[(.*)\]$/)
    {
	$first_bad = $1;
	$first_bad =~ s/^\s*(\S+.*?)\s*$/$1/;
	$info_bad_files{$fields[0]}=$first_bad;
    }
    if ($fields[4] and $fields[4] =~ m/.*\[(.*)\]$/) #it's a new dangerous status
    {
	$second_bad = $1;
	$second_bad =~ s/^\s*(\S+.*?)\s*$/$1/;
	$info_bad_files{$fields[0]}=$second_bad;
    }

    #choose action
    if ($fields[2] and ($fields[2] eq "new"))
    {
	$process and $new_normal_files{$fields[0]}=1; #don't report about new files if we init database for this dir
	$first_bad and $new_bad_files{$fields[0]}=$first_bad;
    }
    elsif ($fields[2] and ($fields[2] eq "removed"))
    {
	$del_normal_files{$fields[0]}=1;
	$first_bad and $del_bad_files{$fields[0]}=$first_bad;
    }
    elsif ($fields[2] and ($fields[2] eq "changed"))
    {
	$fields[3] =~ s/^old\s+//;#remove 'old' prefix
	$fields[4] =~ s/^new\s+//;#remove 'new' prefix
	my @old_status  = split / /,$fields[3];
	my @new_status  = split / /,$fields[4];
	foreach (@old_status)
	{
		/(.*?)=(.*)/ and $change_normal_files{$fields[0]}{$1}{"old"}=$2;
	};
	foreach (@new_status)
	{
		/(.*?)=(.*)/ and $change_normal_files{$fields[0]}{$1}{"new"}=$2;
	};

	#also process bad file transformations
	if (not($first_bad) and $second_bad)
	{
		$new_bad_files{$fields[0]}=$second_bad;
	}
	elsif ($first_bad and not($second_bad))
	{
		$del_bad_files{$fields[0]}=$first_bad;
	}
	elsif ($first_bad and $second_bad)
	{
		my %old_bad_status = ();
		my %new_bad_status = ();

		foreach (split / /,$first_bad)
		{
		    if (/\s*(.*)\((.*)\)/)
		    {
			$old_bad_status{$1}=$_;
		    }
		    else
		    {
			$old_bad_status{$_}=$_;
		    }
		}
		foreach (split / /,$second_bad)
		{
		    if (/\s*(.*)\((.*)\)/)
		    {
			$new_bad_status{$1}=$_;
		    }
		    else
		    {
			$new_bad_status{$_}=$_;
		    }
		}

		my $out="";
		my @bad_fields=("suid","sgid","ww");
		foreach (@bad_fields)
		{
		    (not($old_bad_status{$_}) and $new_bad_status{$_}) and $out .= " +$new_bad_status{$_}";
		    ($old_bad_status{$_} and not($new_bad_status{$_})) and $out .= " -$old_bad_status{$_}";
		}
		if ($change_normal_files{$fields[0]}{"uid"} and $old_bad_status{"suid"} and $new_bad_status{"suid"})
		{
		    $out .=" suid($change_normal_files{$fields[0]}{uid}{old}->";
		    $out .="$change_normal_files{$fields[0]}{uid}{new})";
		}
		if ($change_normal_files{$fields[0]}{"gid"} and $old_bad_status{"sgid"} and $new_bad_status{"sgid"})
		{
		    $out .=" sgid($change_normal_files{$fields[0]}{gid}{old}->";
		    $out .="$change_normal_files{$fields[0]}{gid}{new})";
		}
		$change_bad_files{$fields[0]}=$out;
	}
    }
}

#additional check for checksum changes in bad files
foreach (keys %change_normal_files)
{
    ($change_normal_files{$_}{"md5sum"} and $info_bad_files{$_}) and $change_bad_files{$_} .=" md5sum";
}

#additional check for files became symlinks
foreach (keys %del_normal_files)
{
    if(-l $_)
    {
	$symlinks{$_}=readlink($_);
	delete $del_normal_files{$_};
	$change_normal_files{$_} and delete $change_normal_files{$_};
    }
}

my $date = `LC_ALL=C LANG=C LANGUAGE=C date`;
chomp $date;
print "\nThis is a report generated by osec at '$date'\n\n";


#has any bad info
if (%new_bad_files or %del_bad_files or %change_bad_files)
{
     print "-- PLEASE PAY ATTENTION TO --\n";
     if (%new_bad_files)
     {
        print "New dangerous files :\n";
	print "\t- $_ is $new_bad_files{$_}\n" foreach (sort keys %new_bad_files);
     }

     if (%del_bad_files)
     {
        print "Removed from dangerous files list:\n";
	print "\t- $_ was $del_bad_files{$_}\n" foreach (sort keys %del_bad_files);
     }

     if (%change_bad_files)
     {
        print "Changed dangerous files:\n";
	print "\t- $_ [ $info_bad_files{$_} ]  $change_bad_files{$_}\n" foreach (sort keys %change_bad_files);
     }
     print "\n";
}

if (%symlinks)
{
    print "These regular files turned into symlinks:\n";
    print "\t- $_ --> $symlinks{$_}\n" foreach (sort keys %symlinks);
    print "\n";
}

if (%new_normal_files)
{
    print "New files added to control:\n";
    print "\t- $_\n" foreach (sort keys %new_normal_files);
}

if (%del_normal_files)
{
    print "Removed from control:\n";
    print "\t- $_\n" foreach (sort keys %del_normal_files);
}

if (%change_normal_files)
{
    print "Changed controlled files:\n";
    foreach (sort keys %change_normal_files)
    {
	print "\t- $_\n";
	my %item=%{$change_normal_files{$_}};
	foreach (keys %item) # print additional info except of md5sum
	{
	    print "\t\t$_: $item{$_}{old} -> $item{$_}{new}\n" unless ($_ eq "md5sum")
	} 
    }
}

if (not(%new_normal_files or %del_normal_files or %change_normal_files or
        %new_bad_files or %del_bad_files or %change_bad_files))
{
    print "No changes\n";
}

print "\n";
