Changeset 245

Show
Ignore:
Timestamp:
04/12/07 14:14:24 (2 years ago)
Author:
kindlund
Message:

Initial changes to Integrity object (to create a Filesystem object).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • honeyclient/branches/exp/kindlund-filesystem/lib/HoneyClient/Agent/Integrity.pm

    r239 r245  
    66#              modifications. 
    77# 
    8 # @author knwang, xkovah, ttruong 
     8# @author knwang, xkovah, ttruong, kindlund 
    99# 
    1010# Copyright (C) 2006 The MITRE Corporation.  All rights reserved. 
     
    3131=head1 NAME 
    3232 
    33 HoneyClient::Agent::Integrity - Responsible for performing static integrity  
    34 checks on the filesystem and registry. (Additionally it calls an external module  
    35 which is responsible for performing real-time checking for new processes which 
    36 are created. 
     33HoneyClient::Agent::Integrity - Perl extension to perform configurable  
     34integrity checks of the Agent VM OS. 
    3735 
    3836=head1 VERSION 
    3937 
    40 0.95 
     38This documentation refers to HoneyClient::Agent::Integrity version 0.95. 
    4139 
    4240=head1 SYNOPSIS 
     41 
     42  use HoneyClient::Agent::Integrity; 
     43  use Data::Dumper; 
     44 
     45  # Create the Integrity object.  Upon creation, the object will 
     46  # be initialized, by performing a baseline of the Agent VM OS. 
     47  my $integrity = HoneyClient::Agent::Integrity->new(); 
     48 
     49  # ... Some time elapses ... 
     50 
     51  # Check the Agent VM, for any violations. 
     52  my $changes = $integrity->check(); 
     53 
     54  if (!defined($changes)) { 
     55      print "No integrity violations have occurred.\n"; 
     56  } else { 
     57      print "System integrity has been compromised:\n"; 
     58      print Dumper($changes); 
     59  } 
     60 
     61  # TODO: Need to update this example. 
     62 
     63  # $changes refers to an array of hashtable references, where 
     64  # each hashtable has the following format: 
     65  # 
     66  # $changes = [ { 
     67  #     # The registry directory name. 
     68  #     'key' => 'HKEY_LOCAL_MACHINE\Software...', 
     69  # 
     70  #     # Indicates if the registry directory was deleted, 
     71  #     # added, or changed. 
     72  #     'status' => 'deleted' | 'added' | 'changed', 
     73  # 
     74  #     # An array containing the list of entries within the 
     75  #     # registry directory that have been deleted, added, or 
     76  #     # changed.  If this array is empty, then the corresponding 
     77  #     # registry directory in the original and new hives contained 
     78  #     # no entries. 
     79  #     'entries'  => [ { 
     80  #         'name' => "\"string\"",  # A (potentially) quoted string;  
     81  #                                  # "@" for default 
     82  #         'new_value' => "string", # New string; maybe undef, if deleted 
     83  #         'old_value' => "string", # Old string; maybe undef, if added 
     84  #     }, ], 
     85  # }, ] 
     86 
     87=head1 DESCRIPTION 
     88 
     89# TODO: This text needs to change. 
    4390 
    4491=head2 INITIALIZATION 
     
    92139use Carp (); 
    93140 
     141 
     142# Include Global Configuration Processing Library 
     143use HoneyClient::Util::Config qw(getVar); 
     144 
     145# Include the Registry Checking Library 
     146use HoneyClient::Agent::Integrity::Registry; 
     147 
     148# Include the File/Directory Search Library 
     149use File::Find qw(find); 
     150 
     151# Include the MD5 Checksum Library 
     152use Digest::MD5; 
     153 
     154# Include the Base 64 Encode/Decode Library 
     155use MIME::Base64 qw(encode_base64 decode_base64); 
     156 
     157# Use Storable Library 
     158use Storable qw(nfreeze thaw dclone); 
     159$Storable::Deparse = 1; 
     160$Storable::Eval = 1; 
     161 
     162# Use Dumper Library 
     163use Data::Dumper; 
     164 
     165# Use Basename Library 
     166use File::Basename qw(dirname); 
     167 
     168# Include Logging Library 
     169use Log::Log4perl qw(:easy); 
     170 
     171####################################################################### 
     172# Module Initialization                                               # 
     173####################################################################### 
     174 
     175BEGIN { 
     176    # Defines which functions can be called externally. 
     177    require Exporter; 
     178    our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION); 
     179 
     180    # Set our package version. 
     181    $VERSION = 0.95; 
     182 
     183    @ISA = qw(Exporter); 
     184 
     185    # Symbols to export on request 
     186    @EXPORT = qw( ); 
     187 
     188    # Items to export into callers namespace by default. Note: do not export 
     189    # names by default without a very good reason. Use EXPORT_OK instead. 
     190    # Do not simply export all your public functions/methods/constants. 
     191 
     192    # This allows declaration use HoneyClient::Agent::Integrity ':all'; 
     193    # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK 
     194    # will save memory. 
     195 
     196    %EXPORT_TAGS = ( 
     197        'all' => [ qw( ) ], 
     198    ); 
     199 
     200    # Symbols to autoexport (:DEFAULT tag) 
     201    @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); 
     202 
     203    $SIG{PIPE} = 'IGNORE'; # Do not exit on broken pipes. 
     204} 
     205our (@EXPORT_OK, $VERSION); 
     206 
    94207=pod 
    95208 
    96209=begin testing 
    97210 
    98 # Make sure HoneyClient::Agent::Integrity loads. 
    99 BEGIN { use_ok('HoneyClient::Agent::Integrity', qw(initAll checkAll initRegistry checkRegistry initFileSystem checkFileSystem)) or diag("Can't load HoneyClient::Util::Config package.  Check to make sure the package library is correctly listed within the path."); } 
    100 require_ok('HoneyClient::Agent::Integrity'); 
    101 #can_ok('HoneyClient::Agent::Integrity', 'new'); 
    102 can_ok('HoneyClient::Agent::Integrity', 'initAll'); 
    103 can_ok('HoneyClient::Agent::Integrity', 'checkAll'); 
    104 can_ok('HoneyClient::Agent::Integrity', 'initFileSystem'); 
    105 can_ok('HoneyClient::Agent::Integrity', 'checkFileSystem'); 
    106 use HoneyClient::Agent::Integrity qw(initAll checkAll initFileSystem checkFileSystem); 
    107  
    108 # Make sure HoneyClient::Util::Config loads. 
    109 BEGIN { use_ok('HoneyClient::Util::Config', qw(getVar)) or diag("Can't load HoneyClient::Util::Config package.  Check to make sure the package library is correctly listed within the path."); } 
     211# Make sure Log::Log4perl loads 
     212BEGIN { use_ok('Log::Log4perl', qw(:nowarn)) 
     213        or diag("Can't load Log::Log4perl package. Check to make sure the package library is correctly listed within the path."); 
     214        
     215        # Suppress all logging messages, since we need clean output for unit testing. 
     216        Log::Log4perl->init({ 
     217            "log4perl.rootLogger"                               => "DEBUG, Buffer", 
     218            "log4perl.appender.Buffer"                          => "Log::Log4perl::Appender::TestBuffer", 
     219            "log4perl.appender.Buffer.min_level"                => "fatal", 
     220            "log4perl.appender.Buffer.layout"                   => "Log::Log4perl::Layout::PatternLayout", 
     221            "log4perl.appender.Buffer.layout.ConversionPattern" => "%d{yyyy-MM-dd HH:mm:ss} %5p [%M] (%F:%L) - %m%n", 
     222        }); 
     223
     224require_ok('Log::Log4perl'); 
     225use Log::Log4perl qw(:easy); 
     226 
     227# Make sure the module loads properly, with the exportable 
     228# functions shared. 
     229BEGIN { use_ok('HoneyClient::Util::Config', qw(getVar setVar))  
     230        or diag("Can't load HoneyClient::Util::Config package.  Check to make sure the package library is correctly listed within the path."); } 
    110231require_ok('HoneyClient::Util::Config'); 
    111232can_ok('HoneyClient::Util::Config', 'getVar'); 
    112 use HoneyClient::Util::Config qw(getVar); 
     233can_ok('HoneyClient::Util::Config', 'setVar'); 
     234use HoneyClient::Util::Config qw(getVar setVar); 
     235 
     236# Suppress all logging messages, since we need clean output for unit testing. 
     237Log::Log4perl->init({ 
     238    "log4perl.rootLogger"                               => "DEBUG, Buffer", 
     239    "log4perl.appender.Buffer"                          => "Log::Log4perl::Appender::TestBuffer", 
     240    "log4perl.appender.Buffer.min_level"                => "fatal", 
     241    "log4perl.appender.Buffer.layout"                   => "Log::Log4perl::Layout::PatternLayout", 
     242    "log4perl.appender.Buffer.layout.ConversionPattern" => "%d{yyyy-MM-dd HH:mm:ss} %5p [%M] (%F:%L) - %m%n", 
     243}); 
     244 
     245# Make sure Data::Dumper loads 
     246BEGIN { use_ok('Data::Dumper') 
     247        or diag("Can't load Data::Dumper package. Check to make sure the package library is correctly listed within the path."); } 
     248require_ok('Data::Dumper'); 
     249use Data::Dumper; 
     250 
     251# Make sure Storable loads 
     252BEGIN { use_ok('Storable', qw(nfreeze thaw dclone)) 
     253        or diag("Can't load Storable package. Check to make sure the package library is correctly listed within the path."); } 
     254require_ok('Storable'); 
     255can_ok('Storable', 'nfreeze'); 
     256can_ok('Storable', 'thaw'); 
     257can_ok('Storable', 'dclone'); 
     258use Storable qw(nfreeze thaw dclone); 
    113259 
    114260# Make sure File::Find loads. 
     
    119265 
    120266# Make sure Digest::MD5 loads. 
    121 #BEGIN { use_ok('Digest::MD5', qw(new)) or diag("Can't load Digest::MD5 package.  Check to make sure the package library is correctly listed within the path."); } 
    122 #require_ok('Digest::MD5'); 
    123 #use Digest::MD5; 
     267#BEGIN { use_ok('Digest::MD5') or diag("Can't load Digest::MD5 package.  Check to make sure the package library is correctly listed within the path."); } 
     268require_ok('Digest::MD5'); 
     269use Digest::MD5; 
    124270 
    125271# Make sure MIME::Base64 loads. 
     
    130276use MIME::Base64 qw(encode_base64 decode_base64); 
    131277 
    132 # Make sure Storable loads. 
    133 BEGIN { use_ok('Storable', qw(dclone nfreeze thaw)) or diag("Can't load Storable package.  Check to make sure the package library is correctly listed within the path."); } 
    134 require_ok('Storable'); 
    135 can_ok('Storable', 'dclone'); 
    136 can_ok('Storable', 'nfreeze'); 
    137 can_ok('Storable', 'thaw'); 
    138 use Storable qw(dclone nfreeze thaw); 
    139  
     278# Make sure File::Basename loads. 
     279BEGIN { use_ok('File::Basename', qw(dirname)) or diag("Can't load File::Basename package.  Check to make sure the package library is correctly listed within the path."); } 
     280require_ok('File::Basename'); 
     281can_ok('File::Basename', 'dirname'); 
     282use File::Basename qw(dirname); 
     283 
     284# Make sure HoneyClient::Agent::Integrity::Registry loads 
     285BEGIN { use_ok('HoneyClient::Agent::Integrity::Registry') 
     286        or diag("Can't load HoneyClient::Agent::Integrity::Registry package. Check to make sure the package library is correctly listed within the path."); } 
     287require_ok('HoneyClient::Agent::Integrity::Registry'); 
     288use HoneyClient::Agent::Integrity::Registry; 
     289 
     290# Make sure HoneyClient::Agent::Integrity loads. 
     291BEGIN { use_ok('HoneyClient::Agent::Integrity') or diag("Can't load HoneyClient::Agent::Integrity package.  Check to make sure the package library is correctly listed within the path."); } 
     292require_ok('HoneyClient::Agent::Integrity'); 
     293use HoneyClient::Agent::Integrity; 
     294 
     295# TODO: Are these still needed? 
    140296###Testing Globals### 
    141297# Directory where the known-good test files are stored 
    142 $test_dir = getVar(name => "test_dir"); 
     298#$test_dir = getVar(name => "test_dir"); 
    143299 
    144300# List of files and directories to check during filesystem checking 
    145 $file_checklist = getVar(name => "file_checklist"); 
     301#$file_checklist = getVar(name => "file_checklist"); 
    146302 
    147303# List of files or directories to exclude if found in subdirs during 
    148304# filesystem check. 
    149 $file_exclude = getVar(name => "file_exclude"); 
     305#$file_exclude = getVar(name => "file_exclude"); 
    150306 
    151307# File where found changes are written to 
    152 $change_file = getVar(name => "change_file"); 
     308#$change_file = getVar(name => "change_file"); 
    153309 
    154310=end testing 
     
    156312=cut 
    157313 
    158 # Include Global Configuration Processing Library 
    159 use HoneyClient::Util::Config qw(getVar); 
    160 use HoneyClient::Agent::Integrity::Registry; 
    161 use File::Find qw(find); 
    162 #use Win32::TieRegistry; 
    163 use Digest::MD5; 
    164 use MIME::Base64; 
    165 use Storable qw(nfreeze thaw dclone); 
    166 $Storable::Deparse = 1; 
    167 $Storable::Eval = 1; 
    168 use Data::Dumper; 
    169 use File::Basename qw(dirname); 
    170  
    171 BEGIN { 
    172     # Defines which functions can be called externally. 
    173     require Exporter; 
    174     our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION); 
    175  
    176     # Set our package version. 
    177     $VERSION = 0.95; 
    178  
    179     @ISA = qw(Exporter); 
    180  
    181     # Symbols to export on request 
    182     @EXPORT = qw(new initAll checkAll); 
    183  
    184     # Items to export into callers namespace by default. Note: do not export 
    185     # names by default without a very good reason. Use EXPORT_OK instead. 
    186     # Do not simply export all your public functions/methods/constants. 
    187  
    188     # Symbols to autoexport (:DEFAULT tag) 
    189     @EXPORT_OK = qw(initAll checkAll); 
    190  
    191 
    192 our (@EXPORT_OK, $VERSION); 
    193  
    194  
    195  
    196 ##################### 
    197 # GLOBALS 
    198 ##################### 
    199  
    200 # Package Global Variable 
    201 our $AUTOLOAD; 
    202  
     314####################################################################### 
     315# Global Configuration Variables                                      # 
     316####################################################################### 
     317 
     318# The global logging object. 
     319our $LOG = get_logger(); 
     320 
     321# TODO: Fix these. 
    203322# These two hack variables are necessary currently in order to get values back  
    204323# out of the functions used with the find() function from File::Find. I can 
     
    249368); 
    250369 
     370=pod 
     371 
     372=head1 DEFAULT PARAMETER LIST 
     373 
     374When an Integrity B<$object> is instantiated using the B<new()> function, 
     375the following parameters are supplied default values.  Each value 
     376can be overridden by specifying the new (key => value) pair into the 
     377B<new()> function, as arguments. 
     378 
     379=head2 bypass_baseline  
     380 
     381=over 4 
     382 
     383When set to 1, the object will forgo any type of initial baselining 
     384process, upon initialization.  Otherwise, baselining will occur 
     385as normal, upon initialization. 
     386 
     387=back 
     388 
     389=cut 
    251390 
    252391my %PARAMS = ( 
     392    # When set to 1, the object will forgo any type of initial baselining 
     393    # process, upon initialization.  Otherwise, baselining will occur 
     394    # as normal, upon initialization. 
     395    bypass_baseline => 0, 
    253396 
    254397    # Contains the Registry object, once initialized. 
     398    # (For internal use only.) 
    255399    _registry => undef, 
    256400 
     
    281425); 
    282426 
    283  
    284  
    285  
    286 ################################################################################ 
     427####################################################################### 
     428# Private Methods Implemented                                         # 
     429####################################################################### 
     430 
     431# Helper function, designed to return the list of directories that need 
     432# to be scanned. 
     433
     434# Inputs: None. 
     435# Outputs: array of directories to check 
     436sub _get_directories_to_check(){ 
     437    my $self = shift; 
     438    my @checkdirs; 
     439 
     440    # TODO: This implementation needs further cleanup. 
     441 
     442    # XXX: Sets the VERY hardcoded default for now (late addition for ease of use, not clean code) 
     443    push @checkdirs, "/cygdrive/c/"; 
     444 
     445    # XXX: Can override the default by creating this file (for now, eventually put directly into XML) 
     446    if($self->{file_checklist} ne "none"){ 
     447        open CHECK, "$self->{file_checklist}" or  
     448        die "You need a file named $self->{file_checklist} with fully-qualified " . 
     449            "file and directory names to check in order to run this program. " . 
     450            "Please see the POD documentation for more information.\n"; 
     451        @checkdirs = (); 
     452        $/ = "\n"; 
     453        while(<CHECK>) { 
     454            chomp; 
     455            print "reading in $_ from $self->{file_checklist}\n"; 
     456            push @checkdirs, $_; 
     457        } 
     458        close CHECK; 
     459    } 
     460    return @checkdirs; 
     461
     462 
     463# _found() takes input from $file_checklist, which is a file created by the 
     464# user specifying which directory to baseline and check. As long as the 
     465# directory is not in $file_exclude, then proceed with check. 
     466# Uses the global hash reference hack because 
     467# it's used by find() so can't take in/pass back stuff 
     468sub _found { 
     469         
     470    my $foundfile = $File::Find::name; 
     471     
     472    if (-f $foundfile) { 
     473        if (exists($g_ex_hash->{$foundfile})) { 
     474            return; 
     475        } 
     476 
     477        my $dir = dirname($foundfile); 
     478        while ($dir ne "/") { 
     479            # XXX: Need to add some sort of logic to account 
     480            # for off-by-one key names (i.e., /dir versus /dir/). 
     481            if (exists($g_ex_hash->{$dir})) { 
     482                return; 
     483            } 
     484            $dir = dirname($dir); 
     485        } 
     486 
     487#       if (!exists($g_ex_hash->{$foundfile})) { 
     488            push @{$g_hack}, $foundfile; 
     489#           print "found $foundfile\n"; 
     490#           print "@{$g_hack}\n"; 
     491#       } 
     492    } 
     493
     494 
     495# This function is used to delve down into directories and make sure all the 
     496# contained files are excluded. Uses the global hash reference hack because 
     497# it's used by find() so can't take in/pass back stuff 
     498sub _recursive_exclude{ 
     499     
     500   my $foundfile = $File::Find::name; 
     501    if (-f $foundfile) { 
     502        $g_ex_hash->{$foundfile} = 1; 
     503##      print "\t _recursive_exclude()d $foundfile\n"; 
     504    } 
     505
     506 
     507####################################################################### 
     508# Public Methods Implemented                                          # 
     509####################################################################### 
    287510 
    288511=pod 
    289512 
    290 =head1 EXPORTED FUNCTIONS 
    291  
    292 new() : Creates a new Integrity object. 
     513=head1 METHODS IMPLEMENTED 
     514 
     515The following functions have been implemented by any Integrity object. 
     516 
     517=head2 HoneyClient::Agent::Integrity->new($param => $value, ...) 
     518 
     519=over 4 
     520 
     521Creates a new Integrity object, which contains a hashtable 
     522containing any of the supplied "param => value" arguments. 
     523 
     524I<Inputs>: 
     525 B<$param> is an optional parameter variable. 
     526 B<$value> is $param's corresponding value. 
     527  
     528Note: If any $param(s) are supplied, then an equal number of 
     529corresponding $value(s) B<must> also be specified. 
     530 
     531I<Output>: The instantiated Integrity B<$object>, fully initialized. 
     532 
     533=back 
     534 
     535=begin testing 
     536 
     537diag("These tests will create temporary files in /tmp.  Be sure to cleanup this directory, if any of these tests fail."); 
     538 
     539# Create a generic Integrity object, with test state data. 
     540my $integrity = HoneyClient::Agent::Integrity->new(test => 1, bypass_baseline => 1); 
     541is($integrity->{test}, 1, "new(test => 1, bypass_baseline => 1)") or diag("The new() call failed."); 
     542isa_ok($integrity, 'HoneyClient::Agent::Integrity', "new(test => 1, bypass_baseline => 1)") or diag("The new() call failed."); 
     543 
     544diag("Performing baseline check of the system; this may take some time..."); 
     545 
     546# Perform baseline. 
     547$integrity = HoneyClient::Agent::Integrity->new(); 
     548isa_ok($integrity, 'HoneyClient::Agent::Integrity', "new()") or diag("The new() call failed."); 
     549 
     550=end testing 
    293551 
    294552=cut 
     
    330588    bless $self, $class; 
    331589 
     590    # Perform baselining, if not bypassed. 
     591    if (!$self->{'bypass_baseline'}) { 
     592        $LOG->info("Baselining system."); 
     593        $self->_init(); 
     594    } 
     595 
    332596    # Finally, return the blessed object. 
    333597    return $self; 
     
    340604=head1  
    341605 
    342 initAll() : Takes no input, runs all current init functions. 
     606_init() : Takes no input, runs all current init functions. 
    343607 
    344608=cut 
    345609 
    346 sub initAll
     610sub _init
    347611    my $self = shift; 
    348     # XXX: initRegistry() MUST be called before initFileSystem, since initRegistry 
    349     # creates new files that must exist to be added to the exclusion list for 
    350     # initFileSystem. 
     612    # XXX: The Registry object MUST be created before the Filesystem object, since 
     613    # the Registry object creates new files that must exist to be added to the 
     614    # Filesystem's baseline list of files that exist on the system. 
    351615    $self->{'_registry'} = HoneyClient::Agent::Integrity::Registry->new(); 
    352    $self->initFileSystem(); 
     616    #$self->initFileSystem(); 
    353617} 
    354618 
     
    367631    my $retval; 
    368632 
     633    # One Idea: 
    369634    # If at all possible we want the (faster) registry checks to short circut 
    370635    # the overall checks so we don't have to do the very slow filesystem checks. 
     
    416681change frequently). This file should be named as defined by $self->{file_exclude}. 
    417682 
    418 =begin testing 
     683#=begin testing 
    419684 
    420685#Testing initFileSystem(); 
    421686 
    422 my $ob = HoneyClient::Agent::Integrity->new(); 
    423  
    424 system("mkdir /tmp/hc_test_dir"); 
    425 system("echo hi > /tmp/hc_test_dir/hi.txt"); 
    426 system("echo /tmp/hc_test_dir/hi.txt > $file_checklist"); 
    427 system("echo /tmp/hc_test_dir/hi.txt > $file_exclude"); 
    428 $ob->initFileSystem(); 
    429 open (FILE, "cleanfile.txt") or die "Can't check the cleanfile.txt\n"; 
    430 @result = <FILE>; 
    431 close FILE; 
     687#my $ob = HoneyClient::Agent::Integrity->new(); 
     688
     689#system("mkdir /tmp/hc_test_dir"); 
     690#system("echo hi > /tmp/hc_test_dir/hi.txt"); 
     691#system("echo /tmp/hc_test_dir/hi.txt > $file_checklist"); 
     692#system("echo /tmp/hc_test_dir/hi.txt > $file_exclude"); 
     693#$ob->initFileSystem(); 
     694#open (FILE, "cleanfile.txt") or die "Can't check the cleanfile.txt\n"; 
     695#@result = <FILE>; 
     696#close FILE; 
    432697#Bad test because it will be empty in the case of an error anyway? 
    433 is(scalar(@result), 0, 'initFileSystem: Explicit Filesystem Omission'); 
    434  
    435 system("rm $file_exclude"); 
    436 system("echo hi > /tmp/hc_test_dir/hi.txt"); 
    437 system("echo /tmp/hc_test_dir/hi.txt > $file_checklist"); 
    438 system("echo /tmp/hc_test_dir/ > $file_exclude"); 
    439 $ob->initFileSystem(); 
    440 open (FILE, "cleanfile.txt") or die "Can't check the cleanfile.txt\n"; 
    441 @result = <FILE>; 
    442 close FILE; 
     698#is(scalar(@result), 0, 'initFileSystem: Explicit Filesystem Omission'); 
     699 
     700#system("rm $file_exclude"); 
     701#system("echo hi > /tmp/hc_test_dir/hi.txt"); 
     702#system("echo /tmp/hc_test_dir/hi.txt > $file_checklist"); 
     703#system("echo /tmp/hc_test_dir/ > $file_exclude"); 
     704#$ob->initFileSystem(); 
     705#open (FILE, "cleanfile.txt") or die "Can't check the cleanfile.txt\n"; 
     706#@result = <FILE>; 
     707#close FILE; 
    443708#Bad test because it will be empty in the case of an error anyway? 
    444 is(scalar(@result), 0, 'initFileSystem: Directory Filesystem Omission'); 
    445  
    446 system("rm $file_exclude"); 
    447 system("echo hi > /tmp/hc_test_dir/hi.txt"); 
    448 system("echo /tmp/hc_test_dir/hi.txt > $file_checklist"); 
    449 $ob->initFileSystem(); 
    450 open (DIFF, "diff $test_dir/fs1.txt cleanfile.txt |") or die "Can't check the cleanfile.txt\n"; 
    451 @result = <DIFF>; 
    452 close DIFF; 
     709#is(scalar(@result), 0, 'initFileSystem: Directory Filesystem Omission'); 
     710 
     711#system("rm $file_exclude"); 
     712#system("echo hi > /tmp/hc_test_dir/hi.txt"); 
     713#system("echo /tmp/hc_test_dir/hi.txt > $file_checklist"); 
     714#$ob->initFileSystem(); 
     715#open (DIFF, "diff $test_dir/fs1.txt cleanfile.txt |") or die "Can't check the cleanfile.txt\n"; 
     716#@result = <DIFF>; 
     717#close DIFF; 
    453718#Bad test because it will be empty in the case of an error anyway? 
    454 is(scalar(@result), 0, 'initFileSystem: Known-good file hash'); 
    455  
    456 system("rm -rf /tmp/hc_test_dir/"); 
    457 system("rm $file_checklist"); 
    458  
    459 =end testing 
     719#is(scalar(@result), 0, 'initFileSystem: Known-good file hash'); 
     720 
     721#system("rm -rf /tmp/hc_test_dir/"); 
     722#system("rm $file_checklist"); 
     723 
     724#=end testing 
    460725 
    461726=cut 
     
    497762        else{ 
    498763            #XXX: Does this case matter(exist?) for pipes for instance? 
    499            print "A file that isn't a file or directory (or just general problem, or the file just isn't there) was found with file: $file\n"; 
     764            print "Warning: Ignoring '" . $file . "', since it is not a file or directory.\n"; 
    500765        } 
    501766    } 
     
    542807which occured in the filesystem. 
    543808 
    544 =begin testing 
     809#=begin testing 
    545810 
    546811#Testing that checkFileSystem() 
    547812 
    548 my $ob = HoneyClient::Agent::Integrity->new(); 
    549 my @result; 
     813#my $ob = HoneyClient::Agent::Integrity->new(); 
     814#my @result; 
    550815 
    551816#add 
    552 system("rm $change_file"); 
    553 system("mkdir /tmp/hc_test_dir/"); 
    554 system("echo hi > /tmp/hc_test_dir/hi.txt"); 
    555 system("echo /tmp/hc_test_dir/ > $file_checklist"); 
    556 $ob->initFileSystem(); 
    557 system("echo hi > /tmp/hc_test_dir/hi2.txt"); 
    558 $ob->checkFileSystem(); 
    559 open(CHECK, "diff $test_dir/fs2.txt $change_file |") or die "There was a problem doing the fs2.txt diff";  
     817#system("rm $change_file"); 
     818#system("mkdir /tmp/hc_test_dir/"); 
     819#system("echo hi > /tmp/hc_test_dir/hi.txt"); 
     820#system("echo /tmp/hc_test_dir/ > $file_checklist"); 
     821#$ob->initFileSystem(); 
     822#system("echo hi > /tmp/hc_test_dir/hi2.txt"); 
     823#$ob->checkFileSystem(); 
     824#open(CHECK, "diff $test_dir/fs2.txt $change_file |") or die "There was a problem doing the fs2.txt diff";  
    560825#XXX Won't the die statement just be masked by the redirection of stdout/stderr? 
    561 @result = <CHECK>; 
    562 close(CHECK); 
    563 is(scalar(@result), 0, "checkFileSystem: Files added"); 
     826#@result = <CHECK>; 
     827#close(CHECK); 
     828#is(scalar(@result), 0, "checkFileSystem: Files added"); 
    564829 
    565830 
    566831#delete 
    567 system("rm $change_file"); 
    568 system("rm /tmp/hc_test_dir/hi.txt"); 
    569 $ob->checkFileSystem(); 
    570 open(CHECK, "diff $test_dir/fs3.txt $change_file |") or die "There was a problem doing the fs2.txt diff";  
     832#system("rm $change_file"); 
     833#system("rm /tmp/hc_test_dir/hi.txt"); 
     834#$ob->checkFileSystem(); 
     835#open(CHECK, "diff $test_dir/fs3.txt $change_file |") or die "There was a problem doing the fs2.txt diff";  
    571836#XXX Won't the die statement just be masked by the redirection of stdout/stderr? 
    572 @result = <CHECK>; 
    573 close(CHECK); 
    574 is(scalar(@result), 0, "checkFileSystem: Files deleted"); 
     837#@result = <CHECK>; 
     838#close(CHECK); 
     839#is(scalar(@result), 0, "checkFileSystem: Files deleted"); 
    575840 
    576841#change 
    577 system("rm $change_file"); 
    578 system("echo again >> /tmp/hc_test_dir/hi.txt"); 
    579 $ob->checkFileSystem(); 
    580 open(CHECK, "diff $test_dir/fs4.txt $change_file |") or die "There was a problem doing the fs2.txt diff";  
     842#system("rm $change_file"); 
     843#system("echo again >> /tmp/hc_test_dir/hi.txt"); 
     844#$ob->checkFileSystem(); 
     845#open(CHECK, "diff $test_dir/fs4.txt $change_file |") or die "There was a problem doing the fs2.txt diff";  
    581846#XXX Won't the die statement just be masked by the redirection of stdout/stderr? 
    582 @result = <CHECK>; 
    583 close(CHECK); 
    584 is(scalar(@result), 0, "checkFileSystem: Files changed"); 
    585  
    586 system("rm -rf /tmp/hc_test_dir/"); 
    587 system("rm $file_checklist"); 
    588  
    589 =end testing 
     847#@result = <CHECK>; 
     848#close(CHECK); 
     849#is(scalar(@result), 0, "checkFileSystem: Files changed"); 
     850 
     851#system("rm -rf /tmp/hc_test_dir/"); 
     852#system("rm $file_checklist"); 
     853 
     854#=end testing 
    590855 
    591856=cut 
     
    7361001} 
    7371002 
    738 ################################################################################ 
    739  
    740 sub _get_directories_to_check(){ 
    741 my $self = shift; 
    742 my @checkdirs; 
    743  
    744     #Sets the VERY hardcoded default for now (late addition for ease of use, not clean code) 
    745     push @checkdirs, "/cygdrive/c/"; 
    746  
    747     #Can override the default by creating this file (for now, eventually put directly into XML) 
    748     if($self->{file_checklist} ne "none"){ 
    749         open CHECK, "$self->{file_checklist}" or  
    750         die "You need a file named $self->{file_checklist} with fully-qualified " . 
    751          "file and directory names to check in order to run this program. " . 
    752          "Please see the POD documentation for more information.\n"; 
    753         @checkdirs = (); 
    754         $/ = "\n"; 
    755         while(<CHECK>) { 
    756             chomp; 
    757             print "reading in $_ from $self->{file_checklist}\n"; 
    758             push @checkdirs, $_; 
    759         } 
    760         close CHECK; 
    761     } 
    762     return @checkdirs; 
    763 } 
    764  
    765 ################################################################################ 
    766  
    767 # _found() takes input from $file_checklist, which is a file created by the 
    768 # user specifying which directory to baseline and check. As long as the 
    769 # directory is not in $file_exclude, then proceed with check. 
    770 # Uses the global hash reference hack because 
    771 # it's used by find() so can't take in/pass back stuff 
    772  
    773 sub _found { 
    774          
    775     my $foundfile = $File::Find::name; 
    776      
    777     if (-f $foundfile) { 
    778         if (exists($g_ex_hash->{$foundfile})) { 
    779             return; 
    780         } 
    781  
    782         my $dir = dirname($foundfile); 
    783         while ($dir ne "/") { 
    784             # XXX: Need to add some sort of logic to account 
    785             # for off-by-one key names (i.e., /dir versus /dir/). 
    786             if (exists($g_ex_hash->{$dir})) { 
    787                 return; 
    788             } 
    789             $dir = dirname($dir); 
    790         } 
    791  
    792 #       if (!exists($g_ex_hash->{$foundfile})) { 
    793             push @{$g_hack}, $foundfile; 
    794 #           print "found $foundfile\n"; 
    795 #           print "@{$g_hack}\n"; 
    796 #       } 
    797     } 
    798 } 
    799  
    800 ################################################################################ 
    801  
    802 # This function is used to delve down into directories and make sure all the 
    803 # contained files are excluded. Uses the global hash reference hack because 
    804 # it's used by find() so can't take in/pass back stuff 
    805  
    806 sub _recursive_exclude{ 
    807      
    808    my $foundfile = $File::Find::name; 
    809     if (-f $foundfile) { 
    810         $g_ex_hash->{$foundfile} = 1; 
    811 ##      print "\t _recursive_exclude()d $foundfile\n"; 
    812     } 
    813 } 
    814  
    815 ################################################################################ 
    816  
    817 # Helper function designed to programmatically get or set parameters 
    818 # within this object, through indirect use of the AUTOLOAD function. 
    819 # 
    820 # It's best to explain by example: 
    821 # Assume we have defined a driver object, like the following. 
    822 # 
    823 # use HoneyClient::Agent::Driver; 
    824 # my $driver = Driver->new(someVar => 'someValue'); 
    825 # 
    826 # What this function allows us to do, is programmatically, get or set 
    827 # the 'someVar' parameter, like: 
    828 # 
    829 # my $value = $driver->someVar();    # Gets the value of 'someVar'. 
    830 # my $value = $driver->someVar('2'); # Sets the value of 'someVar' to '2' 
    831 #                                    # and returns '2'. 
    832 # 
    833 # Rather than creating getter/setter functions for every possible parameter, 
    834 # the AUTOLOAD function allows us to create these operations in a generic, 
    835 # reusable fashion. 
    836 # 
    837 # See "Autoloaded Data Methods" in perltoot for more details. 
    838 #  
    839 # Inputs: set a new value (optional) 
    840 # Outputs: the currently set value 
    841 sub AUTOLOAD { 
    842     # Get the object. 
    843     my $self = shift; 
    844  
    845     # Sanity check: Make sure the supplied value is an object. 
    846     my $type = ref($self) or Carp::croak "Error: $self is not an object!\n"; 
    847  
    848     # Now, get the name of the function. 
    849     my $name = $AUTOLOAD; 
    850  
    851     # Strip the fully-qualified portion of the function name. 
    852     $name =~ s/.*://; 
    853  
    854     # Make sure the parameter exists in the object, before we try 
    855     # to get or set it. 
    856     unless (exists $self->{$name}) { 
    857         Carp::croak "Error: Can't access '$name' parameter in class $type!\n"; 
    858     } 
    859  
    860     if (@_) { 
    861         # If we were given an argument, then set the parameter's value. 
    862         return $self->{$name} = shift; 
    863     } else { 
    864         # Else, just return the existing value. 
    865         return $self->{$name}; 
    866     } 
    867 } 
    868  
    869 ################################################################################ 
    870  
    871 # Base destructor function. 
    872 # Since none of our state data ever contains circular references, 
    873 # we can simply leave the garbage collection up to Perl's internal 
    874 # mechanism. 
    875 #sub DESTROY { 
    876 #} 
    877 ################################################################################ 
    878  
    879  
    88010031; 
    8811004 
  • honeyclient/branches/exp/kindlund-filesystem/t/honeyclient_agent_integrity.t

    r131 r245  
    99# =begin testing 
    1010{ 
    11 # Make sure HoneyClient::Agent::Integrity loads. 
    12 BEGIN { use_ok('HoneyClient::Agent::Integrity', qw(initAll checkAll initRegistry checkRegistry initFileSystem checkFileSystem)) or diag("Can't load HoneyClient::Util::Config package.  Check to make sure the package library is correctly listed within the path."); } 
    13 require_ok('HoneyClient::Agent::Integrity'); 
    14 #can_ok('HoneyClient::Agent::Integrity', 'new'); 
    15 can_ok('HoneyClient::Agent::Integrity', 'initAll'); 
    16 can_ok('HoneyClient::Agent::Integrity', 'checkAll'); 
    17 can_ok('HoneyClient::Agent::Integrity', 'initFileSystem'); 
    18 can_ok('HoneyClient::Agent::Integrity', 'checkFileSystem'); 
    19 use HoneyClient::Agent::Integrity qw(initAll checkAll initFileSystem checkFileSystem); 
     11# Make sure Log::Log4perl loads 
     12BEGIN { use_ok('Log::Log4perl', qw(:nowarn)) 
     13        or diag("Can't load Log::Log4perl package. Check to make sure the package library is correctly listed within the path."); 
     14        
     15        # Suppress all logging messages, since we need clean output for unit testing. 
     16        Log::Log4perl->init({ 
     17            "log4perl.rootLogger"                               => "DEBUG, Buffer", 
     18            "log4perl.appender.Buffer"                          => "Log::Log4perl::Appender::TestBuffer", 
     19            "log4perl.appender.Buffer.min_level"                => "fatal", 
     20            "log4perl.appender.Buffer.layout"                   => "Log::Log4perl::Layout::PatternLayout", 
     21            "log4perl.appender.Buffer.layout.ConversionPattern" => "%d{yyyy-MM-dd HH:mm:ss} %5p [%M] (%F:%L) - %m%n", 
     22        }); 
     23
     24require_ok('Log::Log4perl'); 
     25use Log::Log4perl qw(:easy); 
    2026 
    21 # Make sure HoneyClient::Util::Config loads. 
    22 BEGIN { use_ok('HoneyClient::Util::Config', qw(getVar)) or diag("Can't load HoneyClient::Util::Config package.  Check to make sure the package library is correctly listed within the path."); } 
     27# Make sure the module loads properly, with the exportable 
     28# functions shared. 
     29BEGIN { use_ok('HoneyClient::Util::Config', qw(getVar setVar))  
     30        or diag("Can't load HoneyClient::Util::Config package.  Check to make sure the package library is correctly listed within the path."); } 
    2331require_ok('HoneyClient::Util::Config'); 
    2432can_ok('HoneyClient::Util::Config', 'getVar'); 
    25 use HoneyClient::Util::Config qw(getVar); 
     33can_ok('HoneyClient::Util::Config', 'setVar'); 
     34use HoneyClient::Util::Config qw(getVar setVar); 
     35 
     36# Suppress all logging messages, since we need clean output for unit testing. 
     37Log::Log4perl->init({ 
     38    "log4perl.rootLogger"                               => "DEBUG, Buffer", 
     39    "log4perl.appender.Buffer"                          => "Log::Log4perl::Appender::TestBuffer", 
     40    "log4perl.appender.Buffer.min_level"                => "fatal", 
     41    "log4perl.appender.Buffer.layout"                   => "Log::Log4perl::Layout::PatternLayout", 
     42    "log4perl.appender.Buffer.layout.ConversionPattern" => "%d{yyyy-MM-dd HH:mm:ss} %5p [%M] (%F:%L) - %m%n", 
     43}); 
     44 
     45# Make sure Data::Dumper loads 
     46BEGIN { use_ok('Data::Dumper') 
     47        or diag("Can't load Data::Dumper package. Check to make sure the package library is correctly listed within the path."); } 
     48require_ok('Data::Dumper'); 
     49use Data::Dumper; 
     50 
     51# Make sure Storable loads 
     52BEGIN { use_ok('Storable', qw(nfreeze thaw dclone)) 
     53        or diag("Can't load Storable package. Check to make sure the package library is correctly listed within the path."); } 
     54require_ok('Storable'); 
     55can_ok('Storable', 'nfreeze'); 
     56can_ok('Storable', 'thaw'); 
     57can_ok('Storable', 'dclone'); 
     58use Storable qw(nfreeze thaw dclone); 
    2659 
    2760# Make sure File::Find loads. 
     
    3265 
    3366# Make sure Digest::MD5 loads. 
    34 #BEGIN { use_ok('Digest::MD5', qw(new)) or diag("Can't load Digest::MD5 package.  Check to make sure the package library is correctly listed within the path."); } 
    35 #require_ok('Digest::MD5'); 
    36 #use Digest::MD5; 
     67#BEGIN { use_ok('Digest::MD5') or diag("Can't load Digest::MD5 package.  Check to make sure the package library is correctly listed within the path."); } 
     68require_ok('Digest::MD5'); 
     69use Digest::MD5; 
    3770 
    3871# Make sure MIME::Base64 loads. 
     
    4376use MIME::Base64 qw(encode_base64 decode_base64); 
    4477 
    45 # Make sure Storable loads. 
    46 BEGIN { use_ok('Storable', qw(dclone nfreeze thaw)) or diag("Can't load Storable package.  Check to make sure the package library is correctly listed within the path."); } 
    47 require_ok('Storable'); 
    48 can_ok('Storable', 'dclone'); 
    49 can_ok('Storable', 'nfreeze'); 
    50 can_ok('Storable', 'thaw'); 
    51 use Storable qw(dclone nfreeze thaw); 
     78# Make sure File::Basename loads. 
     79BEGIN { use_ok('File::Basename', qw(dirname)) or diag("Can't load File::Basename package.  Check to make sure the package library is correctly listed within the path."); } 
     80require_ok('File::Basename'); 
     81can_ok('File::Basename', 'dirname'); 
     82use File::Basename qw(dirname); 
    5283 
     84# Make sure HoneyClient::Agent::Integrity::Registry loads 
     85BEGIN { use_ok('HoneyClient::Agent::Integrity::Registry') 
     86        or diag("Can't load HoneyClient::Agent::Integrity::Registry package. Check to make sure the package library is correctly listed within the path."); } 
     87require_ok('HoneyClient::Agent::Integrity::Registry'); 
     88use HoneyClient::Agent::Integrity::Registry; 
     89 
     90# Make sure HoneyClient::Agent::Integrity loads. 
     91BEGIN { use_ok('HoneyClient::Agent::Integrity') or diag("Can't load HoneyClient::Agent::Integrity package.  Check to make sure the package library is correctly listed within the path."); } 
     92require_ok('HoneyClient::Agent::Integrity'); 
     93use HoneyClient::Agent::Integrity; 
     94 
     95# TODO: Are these still needed? 
    5396###Testing Globals### 
    5497# Directory where the known-good test files are stored 
    55 $test_dir = getVar(name => "test_dir"); 
     98#$test_dir = getVar(name => "test_dir"); 
    5699 
    57100# List of files and directories to check during filesystem checking 
    58 $file_checklist = getVar(name => "file_checklist"); 
     101#$file_checklist = getVar(name => "file_checklist"); 
    59102 
    60103# List of files or directories to exclude if found in subdirs during 
    61104# filesystem check. 
    62 $file_exclude = getVar(name => "file_exclude"); 
     105#$file_exclude = getVar(name => "file_exclude"); 
    63106 
    64107# File where found changes are written to 
    65 $change_file = getVar(name => "change_file"); 
     108#$change_file = getVar(name => "change_file"); 
    66109} 
    67110 
     
    70113# =begin testing 
    71114{ 
    72 #Testing initFileSystem(); 
     115diag("These tests will create temporary files in /tmp.  Be sure to cleanup this directory, if any of these tests fail."); 
    73116 
    74 my $ob = HoneyClient::Agent::Integrity->new(); 
     117# Create a generic Integrity object, with test state data. 
     118my $integrity = HoneyClient::Agent::Integrity->new(test => 1, bypass_baseline => 1); 
     119is($integrity->{test}, 1, "new(test => 1, bypass_baseline => 1)") or diag("The new() call failed."); 
     120isa_ok($integrity, 'HoneyClient::Agent::Integrity', "new(test => 1, bypass_baseline => 1)") or diag("The new() call failed."); 
    75121 
    76 system("mkdir /tmp/hc_test_dir"); 
    77 system("echo hi > /tmp/hc_test_dir/hi.txt"); 
    78 system("echo /tmp/hc_test_dir/hi.txt > $file_checklist"); 
    79 system("echo /tmp/hc_test_dir/hi.txt > $file_exclude"); 
    80 $ob->initFileSystem(); 
    81 open (FILE, "cleanfile.txt") or die "Can't check the cleanfile.txt\n"; 
    82 @result = <FILE>; 
    83 close FILE; 
    84 #Bad test because it will be empty in the case of an error anyway? 
    85 is(scalar(@result), 0, 'initFileSystem: Explicit Filesystem Omission'); 
     122diag("Performing baseline check of the system; this may take some time..."); 
    86123 
    87 system("rm $file_exclude"); 
    88 system("echo hi > /tmp/hc_test_dir/hi.txt"); 
    89 system("echo /tmp/hc_test_dir/hi.txt > $file_checklist"); 
    90 system("echo /tmp/hc_test_dir/ > $file_exclude"); 
    91 $ob->initFileSystem(); 
    92 open (FILE, "cleanfile.txt") or die "Can't check the cleanfile.txt\n"; 
    93 @result = <FILE>; 
    94 close FILE; 
    95 #Bad test because it will be empty in the case of an error anyway? 
    96 is(scalar(@result), 0, 'initFileSystem: Directory Filesystem Omission'); 
    97  
    98 system("rm $file_exclude"); 
    99 system("echo hi > /tmp/hc_test_dir/hi.txt"); 
    100 system("echo /tmp/hc_test_dir/hi.txt > $file_checklist"); 
    101 $ob->initFileSystem(); 
    102 open (DIFF, "diff $test_dir/fs1.txt cleanfile.txt |") or die "Can't check the cleanfile.txt\n"; 
    103 @result = <DIFF>; 
    104 close DIFF; 
    105 #Bad test because it will be empty in the case of an error anyway? 
    106 is(scalar(@result), 0, 'initFileSystem: Known-good file hash'); 
    107  
    108 system("rm -rf /tmp/hc_test_dir/"); 
    109 system("rm $file_checklist"); 
    110 
    111  
    112  
    113  
    114 # =begin testing 
    115 
    116 #Testing that checkFileSystem() 
    117  
    118 my $ob = HoneyClient::Agent::Integrity->new(); 
    119 my @result; 
    120  
    121 #add 
    122 system("rm $change_file"); 
    123 system("mkdir /tmp/hc_test_dir/"); 
    124 system("echo hi > /tmp/hc_test_dir/hi.txt"); 
    125 system("echo /tmp/hc_test_dir/ > $file_checklist"); 
    126 $ob->initFileSystem(); 
    127 system("echo hi > /tmp/hc_test_dir/hi2.txt"); 
    128 $ob->checkFileSystem(); 
    129 open(CHECK, "diff $test_dir/fs2.txt $change_file |") or die "There was a problem doing the fs2.txt diff";  
    130 #XXX Won't the die statement just be masked by the redirection of stdout/stderr? 
    131 @result = <CHECK>; 
    132 close(CHECK); 
    133 is(scalar(@result), 0, "checkFileSystem: Files added"); 
    134  
    135  
    136 #delete 
    137 system("rm $change_file"); 
    138 system("rm /tmp/hc_test_dir/hi.txt"); 
    139 $ob->checkFileSystem(); 
    140 open(CHECK, "diff $test_dir/fs3.txt $change_file |") or die "There was a problem doing the fs2.txt diff";  
    141 #XXX Won't the die statement just be masked by the redirection of stdout/stderr? 
    142 @result = <CHECK>; 
    143 close(CHECK); 
    144 is(scalar(@result), 0, "checkFileSystem: Files deleted"); 
    145  
    146 #change 
    147 system("rm $change_file"); 
    148 system("echo again >> /tmp/hc_test_dir/hi.txt"); 
    149 $ob->checkFileSystem(); 
    150 open(CHECK, "diff $test_dir/fs4.txt $change_file |") or die "There was a problem doing the fs2.txt diff";  
    151 #XXX Won't the die statement just be masked by the redirection of stdout/stderr? 
    152 @result = <CHECK>; 
    153 close(CHECK); 
    154 is(scalar(@result), 0, "checkFileSystem: Files changed"); 
    155  
    156 system("rm -rf /tmp/hc_test_dir/"); 
    157 system("rm $file_checklist"); 
     124# Perform baseline. 
     125$integrity = HoneyClient::Agent::Integrity->new(); 
     126isa_ok($integrity, 'HoneyClient::Agent::Integrity', "new()") or diag("The new() call failed."); 
    158127} 
    159128