Changeset 245
- Timestamp:
- 04/12/07 14:14:24 (2 years ago)
- Files:
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
honeyclient/branches/exp/kindlund-filesystem/lib/HoneyClient/Agent/Integrity.pm
r239 r245 6 6 # modifications. 7 7 # 8 # @author knwang, xkovah, ttruong 8 # @author knwang, xkovah, ttruong, kindlund 9 9 # 10 10 # Copyright (C) 2006 The MITRE Corporation. All rights reserved. … … 31 31 =head1 NAME 32 32 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. 33 HoneyClient::Agent::Integrity - Perl extension to perform configurable 34 integrity checks of the Agent VM OS. 37 35 38 36 =head1 VERSION 39 37 40 0.95 38 This documentation refers to HoneyClient::Agent::Integrity version 0.95. 41 39 42 40 =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. 43 90 44 91 =head2 INITIALIZATION … … 92 139 use Carp (); 93 140 141 142 # Include Global Configuration Processing Library 143 use HoneyClient::Util::Config qw(getVar); 144 145 # Include the Registry Checking Library 146 use HoneyClient::Agent::Integrity::Registry; 147 148 # Include the File/Directory Search Library 149 use File::Find qw(find); 150 151 # Include the MD5 Checksum Library 152 use Digest::MD5; 153 154 # Include the Base 64 Encode/Decode Library 155 use MIME::Base64 qw(encode_base64 decode_base64); 156 157 # Use Storable Library 158 use Storable qw(nfreeze thaw dclone); 159 $Storable::Deparse = 1; 160 $Storable::Eval = 1; 161 162 # Use Dumper Library 163 use Data::Dumper; 164 165 # Use Basename Library 166 use File::Basename qw(dirname); 167 168 # Include Logging Library 169 use Log::Log4perl qw(:easy); 170 171 ####################################################################### 172 # Module Initialization # 173 ####################################################################### 174 175 BEGIN { 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 } 205 our (@EXPORT_OK, $VERSION); 206 94 207 =pod 95 208 96 209 =begin testing 97 210 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 212 BEGIN { 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 } 224 require_ok('Log::Log4perl'); 225 use Log::Log4perl qw(:easy); 226 227 # Make sure the module loads properly, with the exportable 228 # functions shared. 229 BEGIN { 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."); } 110 231 require_ok('HoneyClient::Util::Config'); 111 232 can_ok('HoneyClient::Util::Config', 'getVar'); 112 use HoneyClient::Util::Config qw(getVar); 233 can_ok('HoneyClient::Util::Config', 'setVar'); 234 use HoneyClient::Util::Config qw(getVar setVar); 235 236 # Suppress all logging messages, since we need clean output for unit testing. 237 Log::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 246 BEGIN { 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."); } 248 require_ok('Data::Dumper'); 249 use Data::Dumper; 250 251 # Make sure Storable loads 252 BEGIN { 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."); } 254 require_ok('Storable'); 255 can_ok('Storable', 'nfreeze'); 256 can_ok('Storable', 'thaw'); 257 can_ok('Storable', 'dclone'); 258 use Storable qw(nfreeze thaw dclone); 113 259 114 260 # Make sure File::Find loads. … … 119 265 120 266 # 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."); } 268 require_ok('Digest::MD5'); 269 use Digest::MD5; 124 270 125 271 # Make sure MIME::Base64 loads. … … 130 276 use MIME::Base64 qw(encode_base64 decode_base64); 131 277 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. 279 BEGIN { 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."); } 280 require_ok('File::Basename'); 281 can_ok('File::Basename', 'dirname'); 282 use File::Basename qw(dirname); 283 284 # Make sure HoneyClient::Agent::Integrity::Registry loads 285 BEGIN { 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."); } 287 require_ok('HoneyClient::Agent::Integrity::Registry'); 288 use HoneyClient::Agent::Integrity::Registry; 289 290 # Make sure HoneyClient::Agent::Integrity loads. 291 BEGIN { 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."); } 292 require_ok('HoneyClient::Agent::Integrity'); 293 use HoneyClient::Agent::Integrity; 294 295 # TODO: Are these still needed? 140 296 ###Testing Globals### 141 297 # Directory where the known-good test files are stored 142 $test_dir = getVar(name => "test_dir");298 #$test_dir = getVar(name => "test_dir"); 143 299 144 300 # List of files and directories to check during filesystem checking 145 $file_checklist = getVar(name => "file_checklist");301 #$file_checklist = getVar(name => "file_checklist"); 146 302 147 303 # List of files or directories to exclude if found in subdirs during 148 304 # filesystem check. 149 $file_exclude = getVar(name => "file_exclude");305 #$file_exclude = getVar(name => "file_exclude"); 150 306 151 307 # File where found changes are written to 152 $change_file = getVar(name => "change_file");308 #$change_file = getVar(name => "change_file"); 153 309 154 310 =end testing … … 156 312 =cut 157 313 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. 319 our $LOG = get_logger(); 320 321 # TODO: Fix these. 203 322 # These two hack variables are necessary currently in order to get values back 204 323 # out of the functions used with the find() function from File::Find. I can … … 249 368 ); 250 369 370 =pod 371 372 =head1 DEFAULT PARAMETER LIST 373 374 When an Integrity B<$object> is instantiated using the B<new()> function, 375 the following parameters are supplied default values. Each value 376 can be overridden by specifying the new (key => value) pair into the 377 B<new()> function, as arguments. 378 379 =head2 bypass_baseline 380 381 =over 4 382 383 When set to 1, the object will forgo any type of initial baselining 384 process, upon initialization. Otherwise, baselining will occur 385 as normal, upon initialization. 386 387 =back 388 389 =cut 251 390 252 391 my %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, 253 396 254 397 # Contains the Registry object, once initialized. 398 # (For internal use only.) 255 399 _registry => undef, 256 400 … … 281 425 ); 282 426 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 436 sub _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 468 sub _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 498 sub _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 ####################################################################### 287 510 288 511 =pod 289 512 290 =head1 EXPORTED FUNCTIONS 291 292 new() : Creates a new Integrity object. 513 =head1 METHODS IMPLEMENTED 514 515 The following functions have been implemented by any Integrity object. 516 517 =head2 HoneyClient::Agent::Integrity->new($param => $value, ...) 518 519 =over 4 520 521 Creates a new Integrity object, which contains a hashtable 522 containing any of the supplied "param => value" arguments. 523 524 I<Inputs>: 525 B<$param> is an optional parameter variable. 526 B<$value> is $param's corresponding value. 527 528 Note: If any $param(s) are supplied, then an equal number of 529 corresponding $value(s) B<must> also be specified. 530 531 I<Output>: The instantiated Integrity B<$object>, fully initialized. 532 533 =back 534 535 =begin testing 536 537 diag("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. 540 my $integrity = HoneyClient::Agent::Integrity->new(test => 1, bypass_baseline => 1); 541 is($integrity->{test}, 1, "new(test => 1, bypass_baseline => 1)") or diag("The new() call failed."); 542 isa_ok($integrity, 'HoneyClient::Agent::Integrity', "new(test => 1, bypass_baseline => 1)") or diag("The new() call failed."); 543 544 diag("Performing baseline check of the system; this may take some time..."); 545 546 # Perform baseline. 547 $integrity = HoneyClient::Agent::Integrity->new(); 548 isa_ok($integrity, 'HoneyClient::Agent::Integrity', "new()") or diag("The new() call failed."); 549 550 =end testing 293 551 294 552 =cut … … 330 588 bless $self, $class; 331 589 590 # Perform baselining, if not bypassed. 591 if (!$self->{'bypass_baseline'}) { 592 $LOG->info("Baselining system."); 593 $self->_init(); 594 } 595 332 596 # Finally, return the blessed object. 333 597 return $self; … … 340 604 =head1 341 605 342 initAll() : Takes no input, runs all current init functions.606 _init() : Takes no input, runs all current init functions. 343 607 344 608 =cut 345 609 346 sub initAll{610 sub _init { 347 611 my $self = shift; 348 # XXX: initRegistry() MUST be called before initFileSystem, since initRegistry349 # creates new files that must exist to be added to the exclusion list for350 # 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. 351 615 $self->{'_registry'} = HoneyClient::Agent::Integrity::Registry->new(); 352 $self->initFileSystem();616 #$self->initFileSystem(); 353 617 } 354 618 … … 367 631 my $retval; 368 632 633 # One Idea: 369 634 # If at all possible we want the (faster) registry checks to short circut 370 635 # the overall checks so we don't have to do the very slow filesystem checks. … … 416 681 change frequently). This file should be named as defined by $self->{file_exclude}. 417 682 418 =begin testing683 #=begin testing 419 684 420 685 #Testing initFileSystem(); 421 686 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; 432 697 #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; 443 708 #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; 453 718 #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 testing719 #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 460 725 461 726 =cut … … 497 762 else{ 498 763 #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"; 500 765 } 501 766 } … … 542 807 which occured in the filesystem. 543 808 544 =begin testing809 #=begin testing 545 810 546 811 #Testing that checkFileSystem() 547 812 548 my $ob = HoneyClient::Agent::Integrity->new();549 my @result;813 #my $ob = HoneyClient::Agent::Integrity->new(); 814 #my @result; 550 815 551 816 #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"; 560 825 #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"); 564 829 565 830 566 831 #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"; 571 836 #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"); 575 840 576 841 #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"; 581 846 #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 testing847 #@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 590 855 591 856 =cut … … 736 1001 } 737 1002 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}" or750 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 the768 # user specifying which directory to baseline and check. As long as the769 # directory is not in $file_exclude, then proceed with check.770 # Uses the global hash reference hack because771 # it's used by find() so can't take in/pass back stuff772 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 account785 # 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 the803 # contained files are excluded. Uses the global hash reference hack because804 # it's used by find() so can't take in/pass back stuff805 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 parameters818 # 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 set827 # 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 value841 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 try855 # 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 internal874 # mechanism.875 #sub DESTROY {876 #}877 ################################################################################878 879 880 1003 1; 881 1004 honeyclient/branches/exp/kindlund-filesystem/t/honeyclient_agent_integrity.t
r131 r245 9 9 # =begin testing 10 10 { 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 12 BEGIN { 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 } 24 require_ok('Log::Log4perl'); 25 use Log::Log4perl qw(:easy); 20 26 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. 29 BEGIN { 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."); } 23 31 require_ok('HoneyClient::Util::Config'); 24 32 can_ok('HoneyClient::Util::Config', 'getVar'); 25 use HoneyClient::Util::Config qw(getVar); 33 can_ok('HoneyClient::Util::Config', 'setVar'); 34 use HoneyClient::Util::Config qw(getVar setVar); 35 36 # Suppress all logging messages, since we need clean output for unit testing. 37 Log::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 46 BEGIN { 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."); } 48 require_ok('Data::Dumper'); 49 use Data::Dumper; 50 51 # Make sure Storable loads 52 BEGIN { 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."); } 54 require_ok('Storable'); 55 can_ok('Storable', 'nfreeze'); 56 can_ok('Storable', 'thaw'); 57 can_ok('Storable', 'dclone'); 58 use Storable qw(nfreeze thaw dclone); 26 59 27 60 # Make sure File::Find loads. … … 32 65 33 66 # 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."); } 68 require_ok('Digest::MD5'); 69 use Digest::MD5; 37 70 38 71 # Make sure MIME::Base64 loads. … … 43 76 use MIME::Base64 qw(encode_base64 decode_base64); 44 77 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. 79 BEGIN { 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."); } 80 require_ok('File::Basename'); 81 can_ok('File::Basename', 'dirname'); 82 use File::Basename qw(dirname); 52 83 84 # Make sure HoneyClient::Agent::Integrity::Registry loads 85 BEGIN { 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."); } 87 require_ok('HoneyClient::Agent::Integrity::Registry'); 88 use HoneyClient::Agent::Integrity::Registry; 89 90 # Make sure HoneyClient::Agent::Integrity loads. 91 BEGIN { 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."); } 92 require_ok('HoneyClient::Agent::Integrity'); 93 use HoneyClient::Agent::Integrity; 94 95 # TODO: Are these still needed? 53 96 ###Testing Globals### 54 97 # Directory where the known-good test files are stored 55 $test_dir = getVar(name => "test_dir");98 #$test_dir = getVar(name => "test_dir"); 56 99 57 100 # List of files and directories to check during filesystem checking 58 $file_checklist = getVar(name => "file_checklist");101 #$file_checklist = getVar(name => "file_checklist"); 59 102 60 103 # List of files or directories to exclude if found in subdirs during 61 104 # filesystem check. 62 $file_exclude = getVar(name => "file_exclude");105 #$file_exclude = getVar(name => "file_exclude"); 63 106 64 107 # File where found changes are written to 65 $change_file = getVar(name => "change_file");108 #$change_file = getVar(name => "change_file"); 66 109 } 67 110 … … 70 113 # =begin testing 71 114 { 72 #Testing initFileSystem();115 diag("These tests will create temporary files in /tmp. Be sure to cleanup this directory, if any of these tests fail."); 73 116 74 my $ob = HoneyClient::Agent::Integrity->new(); 117 # Create a generic Integrity object, with test state data. 118 my $integrity = HoneyClient::Agent::Integrity->new(test => 1, bypass_baseline => 1); 119 is($integrity->{test}, 1, "new(test => 1, bypass_baseline => 1)") or diag("The new() call failed."); 120 isa_ok($integrity, 'HoneyClient::Agent::Integrity', "new(test => 1, bypass_baseline => 1)") or diag("The new() call failed."); 75 121 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'); 122 diag("Performing baseline check of the system; this may take some time..."); 86 123 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(); 126 isa_ok($integrity, 'HoneyClient::Agent::Integrity', "new()") or diag("The new() call failed."); 158 127 } 159 128
