#!/usr/bin/perl -w use strict; $| = 1; ###################################################### # NAI VirusScan DAT file auto updater # November 2002, Bas Rijniersce, bas@brijn.nu # Guillaume Beaudoin, soli@soli.ca (minor contributions) my $version = "0.4"; # Set verbose to tune output (useful for cron use) my $verbose = 0; #$verbose = 0; # No output at all! Not even on the most serious warning, not recommended #$verbose = 1; # Serious warnings #$verbose = 2; # On update only #$verbose = 3; # Verbose output #$verbose = 4; # Even more $verbose = 5; # Debug output # Location of the required files my $lynx = "/usr/bin/lynx"; my $unzip = "/usr/bin/unzip"; my $md5sum = "/usr/bin/md5sum"; my $naiurl = "ftp://ftp.nai.com"; my $iniurl = "ftp://ftp.nai.com/pub/datfiles/english/update.ini"; my $datdir = "/usr/local/uvscan"; my $binary = "/usr/local/uvscan/uvscan"; my $wget = "/usr/bin/wget"; my $tar = "/bin/tar"; my $ls = "/bin/ls"; my $rm = "/bin/rm"; # Initialize variables my $cur_dat_vers = 0; my $dat_file_name = ""; my $dat_file_path = ""; my $rem_dat_vers = 0; my $cur_eng_vers = 0; my $eng_file_name = ""; my $eng_file_path = ""; my $rem_eng_vers = 0; my $rem_dat_md5 = 0; my $rem_eng_md5 = 0; my $res_md5 = -1; my $res = 0; my $tmp = 0; my $zipfound = 0; my $enginefound = 0; my $result = ""; # Tell who we are print "Naiupdt $version and \n\n" if ($verbose >= 3); # Do some checking on needed binaries # Assuming that ls and rm are in their default locations (valid on != Linux?) print "Checking for tar ..\t\t\t\t" if ($verbose >= 3); if (!(-x $tar)) { print "Tar ($tar) not found, can't continue\n" if ($verbose >= 1); exit(1); } else { print "OK\n" if ($verbose == 3); print "OK ($tar)\n" if ($verbose >= 4); } print "Checking for lynx ..\t\t\t\t" if ($verbose >= 3); if (!(-x $lynx)) { print "Lynx ($lynx) not found, can't continue\n" if ($verbose >= 1); exit(1); } else { print "OK\n" if ($verbose == 3); print "OK ($lynx)\n" if ($verbose >= 4); } print "Checking for wget ..\t\t\t\t" if ($verbose >= 3); if (!(-x $wget)) { print "Wget ($wget) not found, can't continue\n" if ($verbose >= 1); exit(1); } else { print "OK\n" if ($verbose == 3); print "OK ($wget)\n" if ($verbose >= 4); } print "Checking for unzip ..\t\t\t\t" if ($verbose >= 3); if (!(-x $unzip)) { print "Unzip ($unzip) not found, can't continue\n" if ($verbose >= 1); exit(1); } else { print "OK\n" if ($verbose == 3); print "OK ($unzip)\n" if ($verbose >= 4); } print "Checking DAT directory ..\t\t" if ($verbose >= 3); if (!(-w $datdir)) { print "DAT dir ($datdir) not found or writable, can't continue\n" if ($verbose >= 1); exit(1); } else { print "OK\n" if ($verbose == 3); print "OK ($datdir)\n" if ($verbose >= 4); } print "Checking uvscan binary ..\t\t" if ($verbose >= 3); if (!(-x $binary)) { print "Uvscan binary ($binary) not found, can't continue\n" if ($verbose >= 1); exit(1); } else { print "OK\n" if ($verbose == 3); print "OK ($binary)\n" if ($verbose >= 4); } # Get local versions by querying binary open (LOCAL, "$datdir/uvscan --version |"); while () { if ( $_ =~ /.*data file v(\d+) /) { $cur_dat_vers = $1; } if ( $_ =~ /.*engine v(\d+\.\d+\.\d+) /) { $cur_eng_vers = $1; $cur_eng_vers =~ s/\.//g; } } close (LOCAL); print "Checking local Engine version ..\t$cur_eng_vers\n" if ($verbose == 3); print "Checking local Engine version ..\tOK ($cur_eng_vers)\n" if ($verbose >= 4); print "Checking local DAT version ..\t\t$cur_dat_vers\n" if ($verbose == 3); print "Checking local DAT version ..\t\tOK ($cur_dat_vers)\n" if ($verbose >= 4); print "\n" if ($verbose >= 3); # Get remote version by querying update.ini print "Downloading update.ini ..\t\t" if ($verbose >= 3); open (LYNX, "TERM=vt100 $lynx -term=vt100 -dump $iniurl 2>&1 |"); # Lynx needs term type while () { chomp; if (/Alert/g) { print "FAIL\n\n"; print "Unable to connect to $iniurl .. maybe more luck on next run\n" if ($verbose >= 1); exit(1); } # A new section has started or the current section ended if (/^\[.*\]$|^$/) { $zipfound = 0; $enginefound = 0; } # The update.ini file has a section called [ZIP] for the DAT file if (/^\[ZIP\]$/) { $zipfound = 1; } if (/^DATVersion=(\d+).*/ and $zipfound) { $rem_dat_vers = $1; } if (/^FileName=(.*)/ and $zipfound ) { #\w*-\d*\.\w* $dat_file_name = $1; } if (/^FilePath=(.*)/ and $zipfound) { $dat_file_path = $1; } if (/^MD5=(.*)/ and $zipfound) { $rem_dat_md5 = $1; } # The update.ini has a section called Engine-LINUX for the engine file if (/^\[Engine-LINUX\]$/) { $enginefound = 1; } if (/^EngineVersion=(\d+).*/ and $enginefound) { $rem_eng_vers = $1; } if (/^FileName=(.*)/ and $enginefound) { $eng_file_name = $1; } if (/^FilePath=(.*)/ and $enginefound) { $eng_file_path = $1; } if (/^MD5=(.*)/ and $enginefound) { $rem_eng_md5 = $1; } } close(LYNX); print "SUCCESS\n" if ($verbose >= 3); print "Checking remote Engine version ..\t$rem_eng_vers\n" if ($verbose == 3); print "Checking remote Engine version ..\tOK ($rem_eng_vers)\n" if ($verbose >= 4); print "Checking remote DAT version ..\t\t$rem_dat_vers\n" if ($verbose == 3); print "Checking remote DAT version ..\t\tOK ($rem_dat_vers)\n" if ($verbose >= 4); print "\n" if ($verbose >= 3); if ($rem_eng_vers > $cur_eng_vers) { print "Make a backup copy ..\n" if ($verbose >= 3); $res = system("cd $datdir;mkdir -p backup;cp liblnxfv.so.4 license.dat messages.dat backup > /dev/null 2>&1"); print "Downloading newer Engine file ..\t" if ($verbose >= 3); print "Executing ..\n$wget --passive-ftp -T 300 -q -nc $naiurl$eng_file_path$eng_file_name .. " if ($verbose >= 5); $res = system("cd $datdir; $wget --passive-ftp -T 300 -q -nc $naiurl$eng_file_path$eng_file_name > /dev/null 2>&1"); if ($res != 0) { # Download failed somehow, notify and remove downloaded file print "FAIL\n\n" if ($verbose >= 3); print "Download of Engine file failed .. maybe more luck on next run\n\n" if ($verbose >= 1); print "Cleaning up ..\t\t\t\t" if ($verbose >= 3); $res = system("cd $datdir; $rm -f $eng_file_name > /dev/null 2>&1"); if ($res != 0) { print "FAIL\n\n" if ($verbose >= 3); print "Remove of DAT file failed .. Remove manually\n\n" if ($verbose >= 1); } else { print "DONE\n" if ($verbose >= 3); } exit(1); } # File received OK print("\tSUCCESS\n") if ($verbose >= 3); if (-x $md5sum) { print("Checking MD5 signature ..\t\t") if ($verbose >= 3); open (MD5, "$md5sum -b < $datdir/$eng_file_name 2>&1 |") or die; while () { if (/^(\w{32})/) { $res_md5 = $1; } } close (MD5); if ($rem_eng_md5 ne $res_md5) { print "FAIL\n\n" if ($verbose >= 3); print("DAT file MD5 signature does not match\n\n") if ($verbose >= 1); exit(1); } print "OK\n" if ($verbose == 3); print "OK ($res_md5)\n" if ($verbose >= 4); } else { print("MD5 signature of Engine file ignored, $md5sum not found ..\n") if ($verbose >= 1); } print("Unpacking Engine file ..\t\t") if ($verbose >= 3); print("Executing ..\n$unzip -o $eng_file_name .. ") if ($verbose >= 5); $res = system("cd $datdir; $unzip -o $eng_file_name > /dev/null 2>&1"); if ( ($res != 2304) and ($res !=0) ) { # WARNING: Very weird return code for success!! print "FAIL\n\n" if ($verbose >= 3); print "Unpacking of Engine file failed ..\n\n" if ($verbose >= 1); print "Cleaning up .. \t\t\t\t" if ($verbose >= 3); $res = system("cd $datdir; $rm -f $eng_file_name > /dev/null 2>&1"); if ($res != 0) { print "FAIL\n\n" if ($verbose >= 3); print "Remove of Engine file failed .. Remove manually\n\n" if ($verbose >= 1); } else { print "DONE\n" if ($verbose >= 3); } exit(1); } print "\tSUCCESS\n" if ($verbose >= 3); print("Cleaning up Engine file ..\t\t") if ($verbose >= 3); $res = system("cd $datdir; rm -f $eng_file_name > /dev/null 2>&1"); if ($res != 0) { print "FAIL\n\n" if ($verbose >= 3); print "Remove of Engine file failed .. Remove manually\n\n" if ($verbose >= 1); exit(2); } print "DONE\n" if ($verbose >= 3); print "Engine updated from $cur_eng_vers to $rem_eng_vers ..\tSUCCESSFUL\n" if ($verbose >= 2); } else { print ("Your Engine file is up to date ..\tOK\n") if ($verbose == 3); print ("Your Engine file is up to date ..\tOK ($cur_eng_vers)\n") if ($verbose >= 4); } print "\n" if ($verbose >= 3); if ($rem_dat_vers > $cur_dat_vers) { print "Make a backup copy ..\n" if ($verbose >= 3); $res = system("cd $datdir;mkdir -p backup;cp names.dat scan.dat backup > /dev/null 2>&1"); print "Downloading newer DAT file ..\t\t" if ($verbose >= 3); print "Executing ..\n$wget --passive-ftp -T 300 -q -nc $naiurl$dat_file_path$dat_file_name .. " if ($verbose >= 5); $res = system("cd $datdir; $wget --passive-ftp -T 300 -q -nc $naiurl$dat_file_path$dat_file_name > /dev/null 2>&1"); if ($res != 0) { # Download failed somehow, notify and remove downloaded file print "FAIL\n\n" if ($verbose >= 3); print "Download of DAT file failed .. maybe more luck on next run\n\n" if ($verbose >= 1); print "Cleaning up ..\t\t\t\t" if ($verbose >= 3); $res = system("cd $datdir; $rm -f $dat_file_name > /dev/null 2>&1"); if ($res != 0) { print "FAIL\n\n" if ($verbose >= 3); print "Remove of DAT file failed .. Remove manually\n\n" if ($verbose >= 1); } else { print "DONE\n" if ($verbose >= 3); } exit (1); } # File received OK print("SUCCESS\n") if ($verbose >= 3); if (-x $md5sum) { print("Checking MD5 signature ..\t\t") if ($verbose >= 3); open (MD5, "$md5sum -b < $datdir/$dat_file_name 2>&1 |") or die; while () { if (/^(\w{32})/) { $res_md5 = $1; } } close (MD5); if ($rem_dat_md5 ne $res_md5) { print "FAIL\n\n" if ($verbose >= 3); print("DAT file MD5 signature does not match\n\n") if ($verbose >= 1); exit(1); } print "OK\n" if ($verbose == 3); print "OK ($res_md5)\n" if ($verbose >= 4); } else { print("MD5 signature of DAT file ignored, $md5sum not found ..\n") if ($verbose >= 1); } print("Unpacking DAT file ..\t\t\t") if ($verbose >= 3); print("Executing ..\n$unzip -o $dat_file_name .. ") if ($verbose >= 5); $res = system("cd $datdir; $unzip -o $dat_file_name > /dev/null 2>&1"); if ( ($res != 2304) and ($res !=0) ) { # WARNING: Very weird return code for success!! print "FAIL\n\n" if ($verbose >= 3); print "Unpacking of DAT file failed ..\n\n" if ($verbose >= 1); print "Cleaning up .. \t\t\t\t" if ($verbose >= 3); $res = system("cd $datdir; $rm -f $dat_file_name > /dev/null 2>&1"); if ($res != 0) { print "FAIL\n\n" if ($verbose >= 3); print "Remove of DAT file failed .. Remove manually\n\n" if ($verbose >= 1); } else { print "DONE\n" if ($verbose >= 3); } exit(1); } print "\tSUCCESS\n" if ($verbose >= 3); print("Cleaning up DAT file ..\t\t\t") if ($verbose >= 3); print("Fixing DAT file permissions ..\nchmod 755 $datdir/*.dat .. ") if ($verbose >= 5); $res = system("cd $datdir; chmod 755 *.dat > /dev/null 2>&1"); if ($res != 0) { print "FAIL\n\n" if ($verbose >= 3); print "DAT file permission change failed .. Change manually\n\n" if ($verbose >= 1); exit(2); } $res = system("cd $datdir; rm -f $dat_file_name > /dev/null 2>&1"); if ($res != 0) { print "FAIL\n\n" if ($verbose >= 3); print "Remove of DAT file failed .. Remove manually\n\n" if ($verbose >= 1); exit(2); } print "DONE\n" if ($verbose >= 3); print "DAT updated from $cur_dat_vers to $rem_dat_vers ..\t\tSUCCESSFUL\n" if ($verbose >= 2); if ($verbose >= 2) { open (README, "$datdir/readme.txt"); read (README, $tmp, 50000); if ($tmp =~ /={40}.*?(\w{4} Emergency Dat Release.*?)[^=]{2}={40}/s) { print "\n************* EMERGENCY DAT RELEASE **************\n\n"; print "$1"; print "\n************* EMERGENCY DAT RELEASE **************\n"; } close (README); } } else { print ("Your DAT file is up to date ..\t\tOK\n") if ($verbose == 3); print ("Your DAT file is up to date ..\t\tOK ($cur_dat_vers)\n") if ($verbose >= 4); } if ($verbose >= 1) { print "\nPerforming a scan test ..\t\t" if ($verbose >= 3); open (VIRUS,"> $datdir/eicar.com"); print VIRUS "X5O!P%\@AP[4\\PZX54(P^)7CC)7}\$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!\$H+H*"; close VIRUS; $res = 1; open (VIRUS, "$binary --noboot $datdir/eicar.com 2>&1 |"); while () { $res = 0 if (/EICAR/); } close (VIRUS); if ($res == 1) { print "FAIL\n\n" if ($verbose >= 3); print "************** SYSTEM IS NOT SECURE **************\n"; print "*** ***\n"; print "*** Standard EICAR test fail ***\n"; print "*** Your system is not virus secure ***\n"; print "*** ***\n"; print "************** SYSTEM IS NOT SECURE **************\n\n"; print "Restoring original files!\n"; $res = system("cd $datdir;mv backup/* .;rm -rf backup"); } else { print "SUCCESSFUL\n" if ($verbose >= 3); } system("cd $datdir; rm -f $datdir/eicar.com;rm -rf backup"); }