#!/usr/bin/perl
#
# ViperDB v0.6
#
# ViperDB was created as a smaller & faster option to Tripwire.
# Tripwire while being a great product leaves something to be
# desired in the speed department and also, by default tripwire
# generates a report everytime it runs and directs that report
# to an email address. This hinders most people from running 
# Tripwire every few minutes to do a system check. ViperDB 
# however is the answer to this problem. ViperDB does not use
# a fancy all-in-one database to keep records instead, I opted
# to keep it fast and hence decided to go with a plaintext db 
# which is stored in each "watched" directory. By using this 
# there is no real one attack point for a attacker to focus his
# attention on. This coupled with the running of ViperDB every 
# 5 minutes (via cron root job) decreases that likelyhood that
# an attacker will be able to modify your "watched" filesystem
# .while ViperDB is monitoring your system
#
# NOTES:
# 
# 
# PLANNED UPGRADES:
# - Adding of a "protect" function which would "react" to changes
#   made to the filesystem and do whatever it could to maintain
#   the stored filesystem (ie change permissions, owners, and
#   groups back to what they were stored as when -init was run)
# - Adding of a more complex "reporting" system which would 
#   create email to a specified address and would report changes,
#   additions, and deletions
# - Adding of a more complex "system status" function which when
#   a change is detected, would grab info that might be helpful 
#   in determining what caused the change (ie. processes running,
#   users logged in, last few lines from logfiles, etc)
#
# THANKS TO:
# whitetrash, wrlwnd, punkis, & rooster
#
# VERSION HISTORY
# 0.1 - 0.5 - Wrote CreateDB.pl which generates the DBs
#           - Wrote CheckDB.pl which used diff to find changes
#           - Re-Coded to use a "distributed database" instead of
#             one centralized DB.
#           - Re-Coded to use a config file (ViperDB.ini)
#           - Changed to use Assoc. Arrays to speed up processing
#           - Added capability to detect additions & deletions of 
#             files to "watched" directories
#       0.6 - Merged CreateDB.pl & CheckDB.pl into one
#           - Cleaned out debugging code and commented more

# These are the only things you should need to set
$configfile='/usr/local/etc/viperdb.ini';
$logfile='/var/log/ViperDB.log';

###
### You shouldn't have to touch anything below here
###

$startrun=`date`;
chomp $startrun;

# Detect what command line switches were passed and act accordingly
if (@ARGV[0] eq '-init'){
	print "Init Detected. Creating Databases...\n";
	&InitDB;
} elsif (@ARGV[0] eq '-check'){
#	print "Check Detected: Now Checking File Sanity...\n";
	&SysCheck;
} else {
	print "\n\nViperDB v0.6\n";
	print "ERROR: Unrecoignized option or none given.\n";
	print "usage: ViperDB -init -check\n";
	print "     -init    Initializes the ViperDB Databases\n";
	print "     -check   Runs a system file sanity check\n";
}

sub InitDB {
	$runtype='init';
	&CreateDB;
}

sub SysCheck {
	$runtype='check';
	&CreateDB;
	&Compare;	
	&Cleanup;
}

sub CreateDB {
	open (CONFIG, "< $configfile");
		STARTCONFIG:
		$configline=<CONFIG>;
		chomp $configline;
		while ( defined($configline) ) {
			if ( $configline =~ /:/) {
				goto STARTCONFIG;
			} else {
				$wd=$configline;
			
				#Set some Var's based on wether we are initing or checking
				if ($runtype eq 'init') {				
					$ViperDB=$wd . '.ViperDB';
					$tmpfile='/tmp/.ViperDB';
				} else {
					$ViperDB=$wd . '.ViperDB.tmp';
					$tmpfile='/tmp/.ViperDB.tmp';
				}

				# Get a dump of all the current files in the dir and 
				system("ls -laAS $wd|tr -s ' '|grep -v total|grep -v ViperDB>>$tmpfile");

				open (SUPPAHSEKRETDB, "> $ViperDB");
		
					open (BINLIST, "< $tmpfile");
						$line=<BINLIST>;
						chomp $line;
						while ( defined($line) ) {
						($perms,$junk,$uid,$gid,$size,$month,$day,$yearortime,$bname) = split/ /,$line;
						
							# I couldn't figure out a way to just do 3 chops and then reverse the string stored in the variables so...
							# I am doing it this way... SHADDDUP... itz not lame.. itz.. just ... just so kewl you don't know it... 
							$aa       = (chop $perms);
							$ab       = (chop $perms);
							$ac       = (chop $perms);
							$ba       = (chop $perms);
							$bb       = (chop $perms);
							$bc       = (chop $perms);
							$ca       = (chop $perms);
							$cb       = (chop $perms);
							$cc       = (chop $perms);
							$aperms   = $ac . $ab . $aa;
							$gperms   = $bc . $bb . $ba;
							$operms   = $cc . $cb . $ca;
							$filetype = (chop $perms);
			
							# Misc Debuggin Shit
							# print "Binary Name: $bname\n";
							# print "  File Type: $filetype\n";
							# print "  File Size: $size\n";
							# print " File Owner: $uid\n";
							# print " File Group: $gid\n";
							# print "Owner Perms: $operms\n";
							# print "Group Perms: $gperms\n";
							# print "Other Perms: $aperms\n";
					
							print SUPPAHSEKRETDB "$wd$bname,$size,$filetype,$uid,$operms,$gid,$gperms,$aperms,$month,$day,$yearortime\n";
	
							$line=<BINLIST>;
							chomp $line;
						} # While
					close (BINLIST);
				
					# rm the tmpfile
					system("rm -rf $tmpfile");
				
				close(SUPPAHSEKRETDB);
				
				# Change the permissions to only allow root to read...
				system("chmod 400 $ViperDB");
				$configline=<CONFIG>;
				chomp $configline;
	
			} # else...if
		}
	close (CONFIG);
}


sub Compare {
	open (DIRLIST, "< $configfile");
		open (LOG, ">> $logfile");
			print LOG "\n\n      START RUN: $startrun\n";

			READDIRLIST:
			$dirlistline=<DIRLIST>;
			chomp $dirlistline;
			while ( defined($dirlistline) ) {
				if ( $dirlistline =~ /:/) {
					goto READDIRLIST;
				} else {
					$mypath=$dirlistline;
				}
				$RealDB=$mypath . '.ViperDB';
				$ChkDB=$mypath . '.ViperDB.tmp';
	
				# Init some Assoc. Arrays
				%valid = ();
				%check = ();
	
				# Read the RealDB into an Assoc. Array
				open(A, $RealDB);
					while (<A>) {
						($bname,$junk) =  split /,/,$_;
						chomp $bname;
						if ( defined($bname) ) {
							if ( defined($valid{$bname}) ) {
#								print "ERROR:RealDB: Duplicate entry found for $bname.\n";
							} else {
								$valid{$bname} = $_;
							} # if ... else
						} # if
					} # while
				close (A);
	
				# Read the CheckDB into an Assoc. Array
				open(B, $ChkDB);
					while (<B>) {
						($bname,$junk) =  split /,/,$_;
						chomp $bname;
						if ( defined($bname) ) {
							if ( defined($check{$bname}) ) {
#								print "ERROR:CheckDB: Duplicate entry found for $bname.\n";
							} else {
								$check{$bname} = $_;
							} # if ... else
						} # if
					} # while
				close (B);
	
					foreach $bname ( sort keys %valid ) {
						$fileinfoa=$valid{$bname};
						$fileinfob=$check{$bname};
						if($fileinfoa ne $fileinfob) {
							($binnamea,$sizea,$filetypea,$uida,$opermsa,$gida,$gpermsa,$apermsa,$montha,$daya,$yearortimea) = split/,/,$fileinfoa;
							($binnameb,$sizeb,$filetypeb,$uidb,$opermsb,$gidb,$gpermsb,$apermsb,$monthb,$dayb,$yearortimeb) = split/,/,$fileinfob;
							chomp $yearortimea;
							chomp $yearortimeb;
							if( ! defined($binnameb) ){
								print LOG "   FILE DELETED: $binnamea\n";
							} else {
								print LOG "CHANGES TO FILE: $bname\n";
									if($sizeb ne $sizea) {
									print LOG "           SIZE: was $sizea now $sizeb\n";
								}
								if($filetypeb ne $filetypea) {
									print LOG "           TYPE: was $filetypea now $filetypeb\n";
								}
								if($uidb ne $uida) {
									print LOG "          OWNER: was $uida now $uidb\n";
								}
								if($opermsb ne $opermsa) {
									print LOG "    OWNER PERMS: was $opermsa now $opermsb\n";
								}
								if($gidb ne $gida) {
									print LOG "          GROUP: was $gida now $gidb\n";
								}
								if($gpermsb ne $gpermsa) {
									print LOG "    GROUP PERMS: was $gpermsa now $gpermsb\n";
								}
								if($apermsb ne $apermsa) {
									print LOG "      ALL PERMS: was $apermsa now $apermsb\n";
								}
								if($monthb ne $montha || $dayb ne $daya || $yearortimeb ne $yearortimea) {
									print LOG "      TIMESTAMP: was $montha $daya $yearortimea now $monthb $dayb $yearortimeb\n";
								}
								
							} # if ... else
						} # if
					} # foreach
	
					foreach $bname ( sort keys %check ) {
						$fileinfoa=$valid{$bname};
						$fileinfob=$check{$bname};
						($binnamea,$junk) = split/,/,$fileinfoa;
						($binnameb,$junk) = split/,/,$fileinfob;
						if (! defined($binnamea) ) {
							print LOG "       NEW FILE: $binnameb\n";
						}
					} # foreach
				$dirlistline=<DIRLIST>;
			chomp $dirlistline;
		} # While
	close (LOG);
}




sub Cleanup {
	open (CONF, "< $configfile");
		STARTCONF:
		$confline=<CONF>;
		chomp $confline;
		while ( defined($confline) ) {
			if ( $confline =~ /:/) {
				goto STARTCONF;
			} else {
				$rmdir=$confline;
			}
			$tmpDB=$rmdir . '.ViperDB.tmp';
			system("rm -rf $tmpDB");
			$confline=<CONF>;
			chomp $confline;
			
		} # While
	close (CONF);
}
#no stickbitz or SUIDs
