#!/bin/perl # #File Integrity Assessment Tool (l6) #Courtesy of Programmaton, Gestion et Consultation, Informatique, INC. #http://www.pgci.ca #Copyright (C) 1998 gilbert@pgci.ca #$Id: l6,v 1.6 1998/10/16 08:04:42 gilbert Exp $ #This program is free software; you can redistribute it and/or #modify it under the terms of the GNU General Public License #as published by the Free Software Foundation; either version 2 #of the License, or (at your option) any later version. #This program is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU General Public License for more details. #You should have received a copy of the GNU General Public License #along with this program; if not, write to the Free Software #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. use File::Find; use Time::Local; use MD5; use SHA qw(sha_version); $ver = &sha_version(); sub usage { print "Usage: L6 [-d] [-r] [-q] [-m] [-c] [-sha1] [-] [path] [path] ... -d don't descend into lower directories -r descend depth first -q only print pathnames -sha1 use SHA-1 instead of MD5 -m print mtime in standard time format -c print ctime for every string - read path list from standard input Normal output is of the form: path/name//Type inode mode links uid/gid size mtime [ctime] extra where extra is a signature using MD5 or SHA-1 or a device/link. With no arguments at all, this prints a hash of stdin. ";exit(1);} #Variable init $nomd5 = '0'; $quiet = '0'; $nomd5 = '0'; $prune = '0'; $deep = '0'; $time = '0'; $sha1 = '0'; $sp = '0'; $pctime = '0'; while ($_ = $ARGV[0], /^-/) { shift; if (/-d$/) {$prune = '1'; next;} if (/-r$/) {$deep = '1'; next;} if (/-q$/) {$quiet = '1'; next;} if (/-sha1$/) {$sha1 = '1'; $nomd5 = '1';next;} if (/-m$/) {$time = '1'; next;} if (/-c$/) {$pctime = '1'; next;} if (/-$/) {$input = '1'; next;} &usage; } if ($sha1 eq "1") { print "Using digest version $ver, library version $SHA::VERSION\n"; } #lets setup a hash to speed things up while (($name,$passwd,$uid) = getpwent) { $name{$uid} = $name; } while (($name,$passwd,$gid) = getgrent) { $name{$gid} = $name; } #if the input is via STDIN if ($input eq '1') { while () { chomp; if (-f $_) { $File::Find::name = $_; $sp = '0'; &wanted;} if (-d $_) { $dir = $_; $sp = '1'; &find(\&wanted,$_);} else { $_ .= "\n"; push @ARGT, $_;} } } while (@ARGV > 0) { $filesystem = shift(@ARGV); if ((-d $filesystem) && ($prune eq "1")) { #lets hack away any pending slashes $counter = $filesystem =~ tr|/\./||; if ($counter > 1) { $filesystem =~ s/\/*$//; } &wanted; } #in case he decides to prune one file if ((-f $filesystem) && ($prune eq "1")) { $prune = '0'; } if (((-e $filesystem) && ($deep eq "0") && ($prune eq "0")) || ($input eq "1")) { # Traverse desired filesystems &find(\&wanted,$filesystem); } if ((-e $filesystem) && ($deep eq "1") && ($prune eq "0")) { # Traverse desired filesystems depth first &finddepth(\&wanted,$filesystem); } #anything on the cmdline that is junk goes to the bitbucket unless ((-e $filesystem) || ($filesystem eq "-d") || ($filesystem eq "-m") || ($filesystem eq "-q") || ($filesystem eq "-5") || ($filesystem eq "-sha1") || ($filesystem eq "-")) { push @ARGT, $filesystem; } } #lets parse the bitbucket while (@ARGT > 0) { $cmdline = join '',splice(@ARGT,0); $size = length($cmdline); if ($nomd5 eq "0") {$md5 = new MD5;$md5->reset;$extra = $md5->hexhash($cmdline);} if ($sha1 eq "1") {$sha = new SHA;$sha->reset;$extra = unpack("H*", ($sha->hash("$cmdline")));} print "-STANDARD INPUT-\//X - - - \[-,-\] $size bytes $extra\n"; } exit(1); sub wanted { if ($prune eq "1") { #there might be a more elegant way with File::Find::Prune, but I just can't get it to work opendir DIR, "$filesystem"; @allfiles = grep !/^\.\.?$/, map "$filesystem/$_", readdir DIR; closedir DIR; while ($_ = shift(@allfiles)) { ($dev,$ino,$mode,$nlink,$uid,$gid,$size,$mtime,$ctime) = (lstat($_))[0,1,2,3,4,5,7,9,10]; $File::Find::name = $_; &details; } } if ($prune eq "0") {($dev,$ino,$mode,$nlink,$uid,$gid,$size,$mtime,$ctime) = (lstat($_))[0,1,2,3,4,5,7,9,10]; &details; } } sub details { #various file tests $type = "binary" if (-f && -B); $type = "directory" if -d; $type = "fifo" if -p; $type = "socket" if -S; $type = "block_special_file" if -b; $type = "character_special_file" if -c; $type = "text" if (-f && -T); $type = "link" if -l ; #little speed hack for the passwd hash my $uid = $name{$uid}; my $gid = $name{$gid}; my $mode = sprintf "%6.6o",$mode; if ($time eq "1") { $mtime = scalar(localtime($mtime));} else {$mtime = sprintf "%8.8x",$mtime;} if ((($type eq "binary") || ($type eq "text")) && (($nomd5 eq "0") && ($sha1 eq "0") && (-R))) { $md5 = new MD5; open(P,$_); seek(P, 0, 0); $md5->reset; $md5->addfile(P); $extra = $md5->hexdigest; close(P);} if ((($type eq "binary") || ($type eq "text")) && (($nomd5 eq "1") && ($sha1 eq "1") && (-R))) { $sha = new SHA; open(P,$_); seek(P, 0, 0); $sha->reset; $sha->addfile(*P); $extra = unpack("H*", ($sha->digest()));} if ($type eq "link") {$extra = readlink($_);} if ($type eq "fifo") {$extra = " ";} if ($pctime eq "0") {$ctime = "";} if ($quiet eq "1") {print "$File::Find::name\n";} if ($sp eq "1") {$File::Find::name =~ s/^.\///; print "$dir/$File::Find::name\//$type $ino $mode $nlink $uid\/$gid $size bytes $mtime $ctime $extra\n";} else {print "$File::Find::name\//$type $ino $mode $nlink $uid\/$gid $size bytes $mtime $ctime $extra\n";} }