eval 'exec perl -x $0 ${1+"$@"}' # -*-perl-*-
  if 0;
#!perl -w
#
# ======================================================================
# This file is Copyright 1998,1999 by the Purdue Research Foundation and
# may only be used under license.  For terms of the license, see the
# file named COPYRIGHT included with this software release.
# AAFID is a trademark of the Purdue Research Foundation.
# All rights reserved.
# ======================================================================
#
# AAFID2 system
# Agent name:        CheckFilePermissions
# Agent description: Check file permissions
# Author:            The AAFID Agents group, Benjamin Kuperman
#
# History of modifications:
# 
# Known bugs:
# 
# Things to do:
#     . Let the score increment be user selectable
#     . read/write state to files
#     . Add the ability to do per-file scoring style
#     . Add tests for bit masks (set/unset)
#     . Perhaps allow the system to ``learn'' proper permissions
#     . Add support for ACL
# 
# Future work:
# 
# Suggestions made by people:
# 
# Generated automatically by ../bin/makeagent.pl on Tue Sep  7 23:55:42 1999.
# 
###### AUTOMATICALLY GENERATED FILE --- DO NOT EDIT ##########
#

# 10 "CheckFilePermissions.aas"
package CheckFilePermissions;

# Version number
# 13 "CheckFilePermissions.aas"
$VERSION=eval {1.0}; $VERSION=$VERSION;

# Agent parameters
%PARAMETERS=(
             Description        => "Check file permissions",
             CheckPeriod        => eval {60},

             FiltersNeeded => {  }
            );

# Package loading
use AAFID::Agent;
use AAFID::Log;
use AAFID::Common;


use vars qw (
             @ISA
             $VERSION
             %PARAMETERS
            );

# Define the superclass.
 @ISA=qw(AAFID::Agent);

=head1 CheckFilePermissions



=cut

# Preamble code


# Provide an Init function.
sub Init {
  my $self=checkref(shift);
# 26 "CheckFilePermissions.aas"
    # reset the file list to be empty 
    $Params{MyFilesToCheck}={};

    # Reset the file score value list format is:
    # (SUID, SGID, Sticky, user read, user write, user execute, group read,
    # group write, group execute, world read, world write, world execute)
    $Params{MyDefaultScoreValues}=[10,10,10,	# special bits
				   1,3,2,	# user bits
				   1,3,2,	# group bits
				   2,6,4];	# other bits

    # Reset my per-file score list to be empty
    $Params{MyFileScoreList}={};	# want to use a hash

    $Params{MyBitMask}=07777;	# used to strip info from stat

    # By default we will use simple scoring
    $Params{MyScoringFlag}="simple";
    
    # Turn on the debugging log
    $self->Log_activate('debug');
  return $self;
}

# The Check function.
sub Check {
  my $self=checkref(shift);
# 49 "CheckFilePermissions.aas"
  my @files=keys %{$Params{MyFilesToCheck}};
  ##
  # These two variables are those that are returned back to the
  # transciever.  Don't worry about keeping track of the history --
  # that is up to the transciever.  Reset the status back to zero
  # every time as the file permissions might change, and this doesn't
  # need to worry about past state.
  ##
  my $status=0;
  my $message="IncorrectFiles:";

  ##
  # This is for scoring.  It should make sense that having /etc/shadow
  # made readable is more serious that /etc/shadow being made writable
  # by root.  
  ##
  my $scoreValues=();

  ##
  # OK, once through all the files, test and score
  ##
  foreach $file (@files) {
      # Get the score vector
      if (exists $Params{MyFileScoreList}->{$file}) {
	  @scoreValues=@{$Params{MyFileScoreList}->{$file}};
      } else {
	  @scoreValues=@{$Params{MyDefaultScoreValues}};
      }

      # stat the file and get the permission (strip high bits)
      my $perm=($Params{MyBitMask} & (stat($file))[2]);
      $self->Log('debug', "Checking file $file, it has permission "
					    .sprintf("%05o", $perm)."\n");

      # don't bother comparing if the file doesn't exist
      # TODO: Establish a rule for non-existant file (see ADD_FILES)
      next unless $perm;
      
      my $permShouldBe=$Params{MyFilesToCheck}->{$file};
      if ($perm != $permShouldBe) {
      ##
      # score the value
      ##
	  if ($Params{MyScoringFlag} eq "simple") {
	      $self->Log('debug', "Using simple scoring for $file\n");
	      $status++;
	  } else {
	      $self->Log('debug',"Using score vector for $file:("
		      .join(",",@scoreValues).")\n");
	      for (my $index=0; $index<@scoreValues; $index++) {
		  # start high, work down
		  my $value = 2**(@scoreValues-$index-1); 
		  # printf ("%o %o %o %o\n",$perm, $permShouldBe, 
		  #           $value, (($perm ^ $permShouldBe) & $value));
		  if (($perm ^ $permShouldBe) & $value) {
		      $status+=$scoreValues[$index];
		  }
		  # $status++;
	      }
	  }
	  $message.="$file,";
      }
  }
  # kill the trailing ','
  chop $message if $message ne "IncorrectFiles:";

  # reset the status to a meaningful value
  $status=10 if $status>10;
  return ($status, $message);
}
# Agent commands

sub command_REMOVE_FILES {
  my $self=checkref(shift);
  my ($message, %p)=@_;
  if (exists($p{Files}) && 1) {
    my $Files = $p{Files};
# 145 "CheckFilePermissions.aas"
    ###
    # Take a list of files, and remove them from the hash
    #
    # TODO: Consider setting an error if file does not exist
    ###
    foreach $file (@$Files) {
	delete $Params{MyFilesToCheck}->{$file};
	$self->Log('debug', "Removing file $file\n");
    }
    # By default, return undef
    return undef;
  }
}

sub command_ADD_FILES {
  my $self=checkref(shift);
  my ($message, %p)=@_;
  if (exists($p{Permission}) && 1) {
    my $Permission = $p{Permission};
# 120 "CheckFilePermissions.aas"
    ###
    # Currently, this function accepts a list of values of the form:
    # ($perm, @files) where $perm is an octal number and @files is a list
    # of files that are supposed to match this permission mask.
    #
    # NOTE: I am expecting the octal value to be 4 digits in this
    # implementation.  i.e. 07777 because the higher bits are to be used as
    # well.
    ###
    my @list=@$Permission;
    my $perm=shift @list;	# grab the mask

    ###
    # Convert the mask to some internal representation
    # if mulitple formats, consider using and variable selections
    ###
    $perm = ($perm & $Params{MyBitMask});

    # Throw these files into the hash
    foreach $file (@list) {
	$Params{MyFilesToCheck}->{$file}=$perm;
	$self->Log('debug', "Adding file $file with permission".sprintf(" %05o\n",$perm));
    }
    # By default, return undef
    return undef;
  }
}

sub command_ADD_SCORE_VECTOR {
  my $self=checkref(shift);
  my ($message, %p)=@_;
  if (exists($p{Vector}) && 1) {
    my $Vector = $p{Vector};
# 176 "CheckFilePermissions.aas"
    ###
    # Currently, this function accepts a list of values of the form:
    # ([vector], @files) where $perm is an octal number and @files is a list
    # of files that are supposed to match this permission mask.
    #
    # Input similar to Vector=>[[1,2,3,4,5,6,7,8,9,0],"foo","bar"]
    ###
    my @list=@$Vector;
    # This is a reference to a vector.
    my $vector=shift @list;

    foreach $file (@list) {
	$Params{MyFileScoreList}->{$file}=$vector;
	$self->Log('debug', "Associating score (".
		join(',',@{$vector}).") with file $file\n")
    }

# vim:sm:wm=0:set cinkeys="0{,0},:,!^F,o,O,e"
    # By default, return undef
    return undef;
  }
}

sub command_SIMPLE_SCORING {
  my $self=checkref(shift);
  my ($message, %p)=@_;
  if (1) {
    
# 164 "CheckFilePermissions.aas"
    ###
    # Set the scoring to be simple
    ###
    $Params{MyScoringFlag}="simple";
    # By default, return undef
    return undef;
  }
}

sub command_LIST_FILES {
  my $self=checkref(shift);
  my ($message, %p)=@_;
  if (1) {
    
# 156 "CheckFilePermissions.aas"
    ###
    # returns a list of (name, perm, name, perm,...) for the files
    #
    # TODO: Try to output in a format that is usable for input
    ###
  return {Files => join(",", %{$Params{MyFilesToCheck}})};
    # By default, return undef
    return undef;
  }
}

sub command_VECTOR_SCORING {
  my $self=checkref(shift);
  my ($message, %p)=@_;
  if (1) {
    
# 170 "CheckFilePermissions.aas"
    ###
    # Use a vector for scoring
    ###
    $Params{MyScoringFlag}="vector";
    # By default, return undef
    return undef;
  }
}

# End of entity marker
_EndOfEntity;
