#!/usr/bin/perl -w
# version 0.1
# Dean Wilson (2003/08/23)

use strict;
use warnings;
use Data::Dumper;

my $psloc ='/bin/ps';
my $psargs ='-ef';
my $delay = 1;
my $seen_procs; # tally of pids alerted for.

for(;;) {
  my $proctable = get_processes("$psloc $psargs");
  $seen_procs = whittle_processes($proctable, $seen_procs);

  sleep $delay; # so we don't eat processor.
}

#----------------------------------------------#

sub get_processes {
  my $ps = shift;
  my @proc_table;

  open(PSBIN, "$ps |")
    || die "Failed to open '$psloc $psargs' due to '$!'\n";

  #load the process table into memory
  while(<PSBIN>) {
    chomp;
    my @fields = split;
    push(@proc_table, \@fields);
  }

  close PSBIN;

  shift @proc_table;# remove the header.
  return \@proc_table;
}

#----------------------------------------------#

sub whittle_processes {
  my $proctable = shift;
  my $alerted_pids = shift;
  my @root_owned;
  my @other_owned;
  my %pid_users;

  #split into two lists, procs owned by root and procs not owned by root
  foreach my $process_entry (@$proctable) {
    if (@$process_entry->[0] eq 'root') {
      push(@root_owned, $process_entry);
    } else {
      $pid_users{$process_entry->[1]} = $process_entry->[0];
    }
  }

  foreach my $root_proc (sort @root_owned) {
    if ($pid_users{$root_proc->[2]}) {
      unless ($alerted_pids->{$root_proc->[1]}) {
        # change this to whatever format you like
        print "sudo suspected at ", scalar localtime();
        print " '$pid_users{$root_proc->[2]}' ";
        print join(',', @$root_proc), "\n";

      }
      $alerted_pids->{$root_proc->[1]}++;
    }
  }
  return $alerted_pids;
}

#----------------------------------------------#
