#VERSION,1.12
#LASTMOD,10.01.2003
# eval build parameters for web servers

# the stripping of letters from version numbers could be done better
# versions are loaded from the "outdated.db" file, which should be in the plugins directory
# we cheat, as apache is the main one that uses spaces for loaded modules... if there are others we'll have to hard code them

# This software is distributed under the terms of the GPL, which should have been received
# with a copy of this software in the "LICENSE.txt" file.

# CHANGES:
# 1.02  -- Fixed bug where software running that was newere than db would warn as outdated
# 1.03  -- Fixed bug if $V (%VERSIONS) was somehow blank it evaluated as a matched version
# 1.04  -- Rewrote major portions to better handle alpha characters in version strings
# 1.05  -- Rewrote the whole thing. Same reason as the v1.04 version, but better. Really.
# 1.06  -- i have no idea
# 1.07  -- Fixed bug where if server is Apache, any build items were over written.
# 1.08  -- Fixed nasty regex bug in the version eval, and made more efficient. 
#           Pointed out by fr0stman, thx Zeno for assistance
# 1.09  -- Tweaks for the auto-updates
# 1.10  -- Accidental version update, no changes
# 1.11  -- Removing () from version strings
# 1.12  -- Fixed improper matching in version evals such that mod_ssl/ would match to ssl/, reported by Paul Bakker

sub nikto_outdated
{
 my $VFILE="$NIKTO{plugindir}/outdated.db";
 my %VERSIONS=load_versions($VFILE);

 # populate @BUILDITEMS with appropriate values
 # if Apache, split on space...
 if ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner} =~ /apache/i) 
   {
    push(@BUILDITEMS,(split(/ /,$TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner})));
   }
 elsif ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner} =~ /weblogic/i) # strip all the date info from weblogic...
   {
    my @T=split(/ /,$TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner});
    push(@BUILDITEMS,"$T[0]\/$T[1]");
   }
 else
   {
    if ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner} !~ /\s/)        # has no spaces
     { $MATCHSTRING=$TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner}; }
    elsif ($TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner} =~ /\//)     # has spaces and / sepr
     { 
      $MATCHSTRING=$TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner};
      $MATCHSTRING=~s/\s+//g; }
    else                                     # must create  sepr
     {
      # use the last non 0-9 . a-z char as a sepr (' ', '-', '_' etc)
      my $sepr=$TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner};
      $sepr =~ s/[a-zA-Z0-9\.]//gi;
      $sepr=substr($sepr,(length($sepr)-1),1);
      # $sepr=~ s/\s+/ /g;
      # break up ID string on $sepr
      my @T=split(/$sepr/,$TARGETS{$CURRENT_HOST_ID}{ports}{$CURRENT_PORT}{banner});
      # assume last is version...
      for ($i=0;$i<$#T;$i++) { $MATCHSTRING .= "$T[$i] "; }
     }
    $MATCHSTRING =~ s/\s+$//;
    push(@BUILDITEMS,$MATCHSTRING);
    nprint("Server Version String:$MATCHSTRING","d");
   }

 my ($v, $V, $BI, $k) = "";
 $MATCHSTRING =~ s/[\(\)]//g; # get rid of pesky () groups

 foreach $BI (@BUILDITEMS)
  {
   my $have_match=0;
   foreach $V (sort keys %VERSIONS)
    {
     if ($V eq "") { next; }
     if ($BI =~ /^$V/i)  # software name matched
      {
       $have_match=1;
       foreach $k (keys %{ $VERSIONS{$V} }) { if ($k eq "") { next; } $v=$k; }
       if (vereval($v,$BI,$V))  # version check
        { 
         my $msg = $VERSIONS{$V}{$v};
         $msg =~ s/\@RUNNING_VER/$BI/g;
         $msg =~ s/\@CURRENT_VER/$v/g;
         chomp($msg);
         nprint("+ $msg");
        }
      }
    }
  if (!$have_match) { $UPDATES{$BI}=1;}
  }
  
 return;
}

sub load_versions
{
 my @T=();
 my %VERS;
 my $F=$_[0] || return;
 open(VF,"<$F") || die nprint("Cannot open versions file '$F': $!");
 my @V=<VF>;
 close(VF);
 foreach my $line (@V)
 {
  chomp($line);
  if ($line =~ /^\#/) { next; }
  if ($line eq "") { next; }
  if ($line =~ /\#/) { $line=~s/\#.*$//; $line=~s/\s+$//; }
  my @T=parse_csv($line); 
  #    Match   Vers   Message
  $VERS{$T[0]}{$T[1]}=$T[2];
 }
return %VERS;
}

sub vereval
{
 # split both by last char of @_[0], as it is the name to version separator
 my $sepr=substr($_[2],(length($sepr)-1),1);
 nprint("nikto_outdated.plugin: verstring: $_[2], sepr:$sepr","d");

 $CURRENT=lc($_[0]);
 $RUNNING=lc($_[1]);

 nprint("nikto_outdated.plugin: \$CURRENT:$CURRENT:\$RUNNING:$RUNNING:","d");

 my @T=split(/$sepr/,$CURRENT);
 my $CURRENT=$T[$#T]; # should be version...
 @T=split(/$sepr/,$RUNNING);
 my $RUNNING=$T[$#T]; # should be version...

# convert alphas to numerics so we can do a real comparison
 $CURRENT=~ s/([^0-9\.]){1}/"." . ord($1) . "."/eg;
 $RUNNING=~ s/([^0-9\.]){1}/"." . ord($1) . "."/eg;

 $RUNNING =~ s/\.+/\./g;
 $CURRENT =~ s/\.+/\./g;
 $RUNNING =~ s/^\.//;
 $CURRENT =~ s/^\.//;
 $RUNNING =~ s/\.$//;
 $CURRENT =~ s/\.$//;

 nprint("nikto_outdated.plugin: \$CURRENT:$CURRENT:\$RUNNING:$RUNNING\: (after numberifcation)","d");
 
 if (($CUR_ORIG !~ /[a-zA-Z]/) && ($RUN_ORIG !~ /[a-zA-Z]/)) 
 {
  @CUR=split(/\./,$CURRENT);
  @RUN=split(/\./,$RUNNING);
 }
 else 
 {
  @CUR=split(//,$CURRENT);
  @RUN=split(//,$RUNNING);
 }
 
# start with 0... eval each in turn...
 for (my $i=0;$i<=$#CUR;$i++)
  {
   nprint("nikto_outdated.plugin: major compare: \$CUR[$i]:$CUR[$i]: \$RUN[$i]:$RUN[$i]:","d");
   if ($CUR[$i] > $RUN[$i])                  { return 1; } # running is older
   if (($CUR[$i] ne "") && ($RUN[$i] eq "")) { return 1; } # running is older
   if ($CUR[$i] < $RUN[$i])                                # running is newer
     { 
      $UPDATES{$_[1]}=1;
      return 0; 
     }
  }

 return 0;  # running is the same version if we make it here
}

1;
