#!/usr/bin/perl
#########################################
# nessus-plugin-logger.pl
#
# Austin Gilbert
# 2-11-02
#
# This script is to be used to kick off
# a nessus-update-plugins command in verbose
# mode.  It then cuts a log: 
#    /usr/local/var/nessus/logs/plugin-update.log
#
# Then, once the update is finished, if you wish
# the script will send an html enabled email to you
# which tells you which plugins were new, and which
# were updated.
# 
#
###########################################

use Mail::Sendmail;
use Time::Local;

$globdir = "/usr/local/lib/nessus/plugins";
$logfile = '/usr/local/var/nessus/logs/plugin.log';
$errorlogfile= '/usr/local/var/nessus/logs/plugin.error';

###############################
#notification variables...    #
###############################
$plugindir = $globdir;

#put in semicolons or whatever is appropriate for multiple addresses.
$contacts = 'who_gets_notified@where.com';

#incase somebody responds to the notification
$reply_to = 'youremailaddress@here.com';
$smtp_server = 'your-smtp-server-here';
$from = 'Nessus-Watch@artemis2.sjmc.org';

#set this to true if you want an email update
#false to just cut the log.
$notify_updates = 'true';

#get a list of the current plugins...
@current_plugins = glob( "$globdir/*" );
%plugins;
%touched;

#check to make sure that users aren't doing scans.
$num_checks = 0;
while ( &check_daemon() )
{
  # print "Nessus Daemon was busy\n";
  
  if ($num_checks > 5 )
  {
	exit -1;
  }
  
  $num_checks++;
  
  #write busy warning to error log.
  open (ERRORLOG, ">>$errorlogfile") or
     open(ERRORLOG, ">$errorlogfile");
  
  if (ERRORLOG)
  {

	print ERRORLOG '[';
	print ERRORLOG scalar localtime();
	print ERRORLOG '] ';
	print ERRORLOG "Couldn't update plugins, service busy..\n";
	close(ERRORLOG);
  }
  #if the daemon is busy check again in 5 minutes.
  sleep (300);
}###end while

#at this point there should be no connections to the daemon,
# so kill it, do the update plugins, and restart the daemon.


#kill the daemon so nobody can connect in the middle of an update.
#print "Killing nessus daemon\n";
@kill_targets = ();
open(kill_file, "ps -ea |");
while(<kill_file>)
{
	if ( $_ =~ /(\d+).*nessusd/)
	{
		push (@kill_targets, $1);
	}
}

foreach $kill_target (@kill_targets)
{
	`kill -kill $kill_target 2>> /usr/local/var/nessus/logs/plugin.error`;
}


#make a hash for these...
foreach $plug (@current_plugins)
{

  #the file size.
  #alternatively you could drop a md5sum in here...
  @stats = stat($plug);
  $plugins{ $plug } = $stats[7];
}

#open a pipe to the update-plugins command.
$pid = open(PH, "/usr/local/sbin/nessus-update-plugins -v 2>> /usr/local/var/nessus/logs/plugin.error |");

while (<PH>) 
{
	chomp($_);
	#print $_, "\n";

	if (!defined $plugins{ "$globdir/$_" } )
	{
		if ($_ eq "smb_nt.inc" )
		{
			
		  @nt_lines = glob("$globdir/smb_nt*");
		  foreach $ntline (@nt_lines)
		  {
			if (!defined $plugins{ $ntline } )
			{

				push (@new_plugins, $ntline);
			}
			else
			{
			   push (@touched_plugins, $ntline );
			   
			}###end else
			
		  }###end foreach
		}
		else
		{
		  push (@new_plugins, $_ );
		}
		
	}
	else
	{
		push (@touched_plugins, $_ );
	}

}###end while

#get the mod times on the "touched" plugins...
foreach $touch (@touched_plugins)
{
	@stats = stat("$globdir/$touch");
	$touched{ "$globdir/$touch" } = $stats[7];

	#see if it matches the old ones...
	if( $touched{ "$globdir/$touch"} ne $plugins{ "$globdir/$touch" } )
	{
		push (@updated_plugins, $touch);
		
	}###end if
	
}###end foreach
    
#append entries to the log file.
open (LOG, ">>$logfile" ) or
 open(LOG, ">$logfile") or
  die ("couldn't create $logfile $!");


foreach $newone (@new_plugins)
{
	print LOG '[';
	print LOG scalar localtime();
	print LOG '] ';
	print LOG '[';
	print LOG time();
	print LOG '] ';
	print LOG 'Loaded new plugin: ';
	print LOG "$newone\n";
	
}###end foreach

foreach $updatedone (@updated_plugins)
{
	print LOG '[';
	print LOG scalar localtime();
	print LOG '] ';
	print LOG '[';
	print LOG time();
	print LOG '] ';
	print LOG 'Updated plugin: ';
	print LOG "$updatedone\n";
	
}###end foreach

close( LOG );


if ($notify_updates eq 'true')
{
 &do_plugin_alert();
}


#restart the Nessus Daemon
#print "Starting nessus daemon\n";
#`/usr/local/sbin/nessusd -D 2>> /usr/local/var/nessus/logs/plugin.error &`;
system( '/usr/local/sbin/nessusd -D' );
#exit 0; 







##############################
# sub check_daemon
##############################
sub check_daemon
{
	$pid2 = open(PH2, "/bin/netstat 2>&1 /dev/null |");
	while (<PH2>)
	{
	  if ($_ =~ /nessus/ )
	  {
	     close( PH2 );
	     return 1;
          }
	}
	close(PH2);
	return 0;
}


##############################
# sub do_plugin_alert
##############################
sub do_plugin_alert
{
  %new_plugins;
  %update_plugins;
  %months = ('Jan'=>0, 'Feb'=>1 , 'Mar'=> 2,
  	     'Apr'=> 3,'May'=>4 , 'Jun'=> 5,
	     'Jul'=> 6,'Aug'=>7 , 'Sep'=> 8,
	     'Oct'=> 9,'Nov'=>10, 'Dec'=>11 );

  $boundary = "====" . time() . "====";
  %mail = ( 'SMTP' => $smtp_server,
  	    'from' => $from,
	    'reply-to' => $reply_to,
	    'to' => $contacts,
	    'subject' => 'Plugin Update Report',
	    'content-type' => 'text/html; charset="iso-8859-1"',
	   );

  $new_entry_bool = 'false';
  $updated_entry_bool = 'false';

  $log = '/usr/local/var/nessus/logs/plugin.log';
  ($sec, $min, $hour, $mday, $mon, $year, $wday, $day, $isdst) = localtime();

  $target = timelocal( 0, 0, $hour, $mday, $mon, $year );

  open(FILE, "$log") or
  	push (@message, "Couldn't Open the Plugin Log File $log");

  while( <FILE> )
  {
    if ( $_ =~ /\[(.*)\] \[/)
    {
	#compare lines with the target
	($sday, $smon, $day, $hours, $year, $extra) = split(/ /, $1);
	if ($day eq "")
	{
		$day = $hours;
		$hours = $year;
		$year = $extra;
	}###end if

	($hour, $min, $sec) = split(/ /, $hours);
	$test = timelocal( $sec, $min, $hour, $day, $months{$smon}, $year);

	if( $test >= $target )
	{
	  if ($_ =~ /new plugin: (.*)/ )
	  {
		 $new_plugins{$1} = $test;
		 $new_entry_bool = 'true';
	  }###end if
	  elsif( $_ =~ /Updated plugin: (.*)/ )
	  {
		 $updated_plugins{$1} = $test;
		 $updated_entry_bool = 'true';
	  }###end else
	}###end if
     }###end if
  }#end while


  if ($new_entry_bool eq 'true')
  {
     push(@message, "<dl><lh><b>New Plugins:</b></lh>");

     foreach $entry (sort(keys(%new_plugins)))
     {
	my $then = scalar localtime( $new_plugins{ $entry } );
	push (@message, "<li><dt>$then    <b> $entry</b></dt><br/>");

	$description = &lookup_description( $entry );
	push (@message, "<font size='1.5' color='gray'><dd>$description</dd></font></li><br/>");

     }###end foreach

     push( @message, "</dl>");

  }###end if

  if ( $updated_entry_bool eq 'true' )
  {
     push(@message, "<dl><lh><b>Updated Plugins: </b></lh>");
     foreach $entry (sort(keys(%updated_plugins)))
     {
	my $then = scalar localtime($updated_plugins{ $entry } );
	push(@message, "<li><dt>$then    <b> $entry</b></dt><br/>");

	$description = &lookup_description($entry);
	push(@message, "<font size='1.5' color='gray'><dd>$description</dd></font></li><br/>");

     }###end foreach

     push(@message, "</dl>");
  }###end if

  if (defined @message)
  {
     my $html = join('', @message);
     $mail{'body'} = <<END_OF_BODY;
     <html><head/><body>$html</body></html>
END_OF_BODY
 
     sendmail(%mail) || print "Error:  $Mail::Sendmail::error\n";
  }###end if

}###end sub


##########################
# sub lookup_description
##########################
sub lookup_description
{
	open(LOOKUP,"$plugindir/@_[0]" ) or return "";
	my $state = 'looking';
	my $ret = "";

	while( <LOOKUP> )
	{
	  if ( ( $_ =~ /desc\["english"\]/) ||
	       ( $_ =~ /script_description\(enlish/ ) )
	  {
		  $state = 'desc';
		  next;
	  }###end if

	  if ( ( $_ =~ /^Solution/) || ($_ =~ /^Risk factor/) ||
	       ( $_ =~ /desc\["francais"/) ||
	       ( $_ =~ /Solution:/) || ($_ =~ /Risk factor/) )
	  {
		  $state = 'done';
		  last;
          }###end if

	  if ($state eq 'desc')
	  {
	     $ret .= $_ . "<dd>";
	  }
	}###end while

	return $ret;
	
}###end sub
	      
