root/honeyclient/branches/bug/42/lib/HoneyClient/Manager/FW.pm

Revision 113, 99.4 kB (checked in by kindlund, 2 years ago)

Discovered that 'use English;' also kills regular expression performance. Modified this statement to include 'use English -no_match_vars;', to resolve. See http://perldoc.perl.org/perlvar.html for more information.

  • Property svn:keywords set to Id "$file"
Line 
1 #!/usr/bin/perl
2
3 #######################################################################
4 # Created on:  Dec 2, 2005
5 # Package:     HoneyClient::Manager::FW
6 # File:        FW.pm
7 # Description: A SOAP server that provides a way to remotely and dynamically configure iptables rules for all honeyclients.
8 #
9 # @author(s) durick, kindlund, xkovah
10 #
11 # SVN: $Id$
12 #
13 # Copyright (C) 2006 The MITRE Corporation.  All rights reserved.
14 #
15 # This program is free software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation, using version 2
18 # of the License.
19 #
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 # 02110-1301, USA.
29 #
30 #######################################################################
31
32 =pod
33
34 =head1 NAME
35
36 HoneyClient::Manager::FW - Perl module to remotely handle firewall rule/chain creation and deletion
37 which will provide network connectivity for the honeyclients during crawling.  Additionally,
38 it will provide protection when the honeyclients become compromised by enabling static rate limiting(tcp/udp/icmp)
39 and MAC address filtering.
40
41 =head1 VERSION
42
43 This documentation refers to HoneyClient::Manager::FW version 1.0.
44
45 =head1 SYNOPSIS
46
47 =head2 CREATING THE SOAP SERVER
48
49   # Make sure HoneyClient::Util::Config loads properly
50 use HoneyClient::Util::Config qw(getVar);
51
52 # Make sure IPTables::IPv4 loads
53 use IPTables::IPv4;
54
55 # Make sure HoneyClient::Manager::FW can load
56 use HoneyClient::Manager::Firewall::FW;
57
58 # Make sure HoneyClient::Util::SOAP loads properly
59 require_ok('HoneyClient::Util::SOAP');
60
61 package HoneyClient::Manager::Firewall::FW;
62 use HoneyClient::Util::SOAP qw(getClientHandle getServerHandle);
63 my $daemon = getServerHandle();
64 $daemon->handle;
65
66 The SOAP firewall server will  boot up when the honeywall is started by the HoneyClient manager.  The main
67 directory that holds all the listener code is the /hc directory.  startFWListener.pl is located in the /etc/rc.d/rc3.d directory and
68 will boot up when the honeywall starts up in run level three.  After start up, the firewall listener will await calls from the HoneyClient
69 manager so that the firewall may be configured properly and dynamically updated when crawling begins.
70
71 Steps to get honeyclient listening:
72
73 1.  Boot up honeyclient honeywall vmware image.
74 2.  Start up our SOAP firewall and SOAP log listener
75 /usr/bin/perl /hc/startFWListener.pl > /dev/null 2> /dev/null &
76 These will start upon boot of the honeywall so you will not have to do anything except boot the image.
77 3.  Now the firewall is listening for all SOAP client calls
78 4.  Do a "ps -xf" to confirm that your firewall is listening
79     It should show something like:
80     7580 pts/0    S      0:01 /usr/bin/perl /hc/startFWListener.pl
81 5.  Make your FW calls now from honeyclient-client.pl.
82
83 =head2 INTERACTING WITH SOAP SERVER
84
85  use HoneyClient::Util::SOAP qw(getClientHandle);
86  use HoneyClient::Util::Config qw(getVar);
87  
88 After the honeywall boots up, startFWListerner.pl will be executed and begin listening.  From
89 here we want to start interacting with our SOAP FW server.
90  
91  # Create a new SOAP client, to talk to the HoneyClient::Manager::FW module
92  # @initlist will contain all the return values sent back from the server (PID of startFWListerner.pl on server and status message)
93  #  Lets set our default honeyclient ruleset:
94   my $stub = getClientHandle(namespace => "HoneyClient::Manager::FW");
95   my $som = $stub->fwInit();
96   my @initlist = $som->paramsall;
97   print "$_\n" foreach (@initlist);
98  
99  # To dynamically append new rules to the iptables ruleset, do the following
100 $hashref = this data structure will be passed from the manager to the HoneyClient::Manager::FW
101  
102  $som = $stub->addRule( $hashref );
103  print $stub->result;
104  print "\n";
105  
106 # To dynamically delete rules, all you need to do is delete the user-defined chain that was originally created.
107  
108 $som = $stub->deleteChain( $hashref );
109 print $stub->result;
110 print "\n";
111  
112 # To get the status of the current iptables ruleset, this function prints to hard disk the working iptables ruleset
113 $som = $stub->getStatus();
114 print $stub->result;
115 print "\n";
116  
117 # For all new VM's that we plan to add later on, we will have to add new VM chains:
118 $som = $stub->addChain( $hashref);
119 print $stub->result;
120 print "\n";
121
122  # To shutdown the Firewall SOAP listner on the Honeywall
123 $som = $stub->FWShutdown();
124 print $stub->result;
125 print "\n";
126
127 =head1 DESCRIPTION
128
129 Once created, the daemon acts as a stand-alone SOAP server,
130 processing individual requests from the  HoneyClient manager
131 and manipulating the IPTables ruleset on the transparent
132 virtual honeywall.
133
134 =cut
135
136 package HoneyClient::Manager::FW;
137 require Exporter;    #  packages allow a program to be partitioned into separate namespaces
138
139 # Include HoneyClient libraries
140 use HoneyClient::Util::Config qw(getVar);
141 use HoneyClient::Util::SOAP qw(getServerHandle getClientHandle);
142
143 #use HoneyClient::Manager::FW::Argus;
144 #use HoneyClient::Manager::FW::Tcpdump;
145 #use HoneyClient::Manager::FW::Integrity;
146
147 # Include logging library
148 use Log::Log4perl qw(get_logger);
149
150 # Threading libraries
151 use threads;
152 use threads::shared;
153
154 # Additional libraries needed
155 use FileHandle;
156 use IO::File;
157 use IPTables::IPv4;
158 use Config::General;
159 use Data::Dumper;
160 use POSIX qw( WIFEXITED );
161 use English '-no_match_vars';
162
163 # set our configuration file location
164 my $config_file = getVar(name => "config_file");
165
166 # starting up the logging mechanism
167 Log::Log4perl->init($config_file);
168
169 =begin testing
170
171 diag("Beginning of HoneyClient::Manager::FW testing.");
172 diag("Making sure all Modules are present");
173
174 # Make sure Log::Log4perl loads.
175 BEGIN { use_ok('Log::Log4perl') or diag("Can't load Log::Log4perl package.  Check to make sure the package library is correctly listed within the path."); }
176 require_ok('Log::Log4perl');
177 use Log::Log4perl;
178
179 # Make sure Filehandle loads.
180 BEGIN { use_ok('FileHandle') or diag("Can't load FileHandle package.  Check to make sure the package library is correctly listed within the path."); }
181 require_ok('FileHandle');
182 use FileHandle;
183
184 # Make sure IO::File loads.
185 BEGIN { use_ok('IO::File') or diag("Can't load IO::File package.  Check to make sure the package library is correctly listed within the path."); }
186 require_ok('IO::File');
187 use IO::File;
188
189 # Make sure IPTables::IPv4 loads.
190 BEGIN { use_ok('IPTables::IPv4') or diag("Can't load IPTables::IPv4 package.  Check to make sure the package library is correctly listed within the path."); }
191 require_ok('IPTables::IPv4');
192 use IPTables::IPv4;
193
194 # Make sure Config::General loads.
195 BEGIN { use_ok('Config::General') or diag("Can't load Config::General package.  Check to make sure the package library is correctly listed within the path."); }
196 require_ok('Config::General');
197 use Config::General;
198
199 # Make sure use Data::Dumper loads.
200 BEGIN { use_ok('Data::Dumper') or diag("Can't load use Data::Dumper package.  Check to make sure the package library is correctly listed within the path."); }
201 require_ok('Data::Dumper');
202 use Data::Dumper;
203
204 # Make sure use Net::DNS::Resolver loads.
205 BEGIN { use_ok('Net::DNS::Resolver') or diag("Can't load use Net::DNS::Resolver package.  Check to make sure the package library is correctly listed within the path."); }
206 require_ok('Net::DNS::Resolver');
207 use Net::DNS::Resolver;
208
209 # Make sure use Time::HiRes loads.
210 BEGIN { use_ok('Time::HiRes', qw(gettimeofday)) or diag("Can't load use Time::HiRes package.  Check to make sure the package library is correctly listed within the path."); }
211 require_ok('Time::HiRes');
212 can_ok('Time::HiRes', 'gettimeofday');
213 use Time::HiRes qw(gettimeofday);
214
215 # Make sure use English loads.
216 BEGIN { use_ok('English') or diag("Can't load use English package.  Check to make sure the package library is correctly listed within the path."); }
217 require_ok('English');
218 use English '-no_match_vars';
219
220 # Make sure use threads loads.
221 BEGIN { use_ok('threads') or diag("Can't load use threads package.  Check to make sure the package library is correctly listed within the path."); }
222 require_ok('threads');
223 use threads;
224
225 # Make sure HoneyClient::Util::Config loads.
226 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."); }
227 require_ok('HoneyClient::Util::Config');
228 can_ok('HoneyClient::Util::Config', 'getVar');
229 use HoneyClient::Util::Config qw(getVar);
230
231 # Make sure the module loads properly, with the exportable
232 # functions shared.
233 BEGIN { use_ok('HoneyClient::Manager::FW', qw(init_fw destroy_fw _getVMName)) or diag("Can't load HoneyClient::Manager:VM package.  Check to make sure the package library is correctly listed within the path."); }
234 require_ok('HoneyClient::Manager::FW');
235 can_ok('HoneyClient::Manager::FW', 'init_fw');
236 can_ok('HoneyClient::Manager::FW', 'destroy_fw');
237 can_ok('HoneyClient::Manager::FW', '_getVMName');
238 use HoneyClient::Manager::FW qw(init_fw destroy_fw _getVMName);
239
240 # Make sure HoneyClient::Util::SOAP loads.
241 BEGIN { use_ok('HoneyClient::Util::SOAP', qw(getServerHandle getClientHandle)) or diag("Can't load HoneyClient::Util::SOAP package.  Check to make sure the package library is correctly listed within the path."); }
242 require_ok('HoneyClient::Util::SOAP');
243 can_ok('HoneyClient::Util::SOAP', 'getServerHandle');
244 can_ok('HoneyClient::Util::SOAP', 'getClientHandle');
245 use HoneyClient::Util::SOAP qw(getServerHandle getClientHandle);
246
247 # Make sure use Proc::ProcessTable loads.
248 BEGIN { use_ok('Proc::ProcessTable') or diag("Can't load use Proc::ProcessTable package.  Check to make sure the package library is correctly listed within the path."); }
249 require_ok('Proc::ProcessTable');
250 use Proc::ProcessTable;
251
252 diag("Making sure perl and shell scripts exist.\n");
253 ok( "-f /hc/startFWListener.pl", '/hc/startFWListener.pl is present' );
254 ok( "-f /hc/startLogListener.pl", '/hc/startLogListener.pl is present' );
255 ok( "-f /hc/startFWListener.sh", '/hc/startFWListener.sh is present' );
256 ok( "-f /hc/startLogListener.sh", '/hc/startLogListener.sh is present' );
257 ok( "-f  /etc/honeylog.conf", '/etc/honeylog.conf is present' );
258 ok("-f  /etc/honeyclient.conf", '/etc/honeyclient.conf exists');
259 #ok( -f , "/proc/sys/net/ipv4/ip_forward", '/proc/sys/net/ipv4/ip_forward does exist');
260 ok(" -f /etc/resolv.conf", '/etc/resolv.conf file does exist');
261 ok(" -f /etc/syslog.conf", '/etc/syslog.conf file does exist');
262 ok( "-f /usr/bin/uptime", '/usr/bin/uptime is present' );
263 ok(" -f /bin/uname", '/bin/uname exists');
264 ok(" -f /bin/mail", 'mail() exists');
265 ok(" -f /sbin/iptables", 'IPTables binary does exist');
266 diag("Enabling test hash reference here");
267 my $hashref = {
268
269     'foo' => {   
270         'targets' => {   
271             'rcf.mitre.org'   => { 'tcp' => [ 80 ], },
272         },
273
274         'resources' => {
275             'http://www.mitre.org' => 1,
276         },
277         'sources' => {
278
279             '00:0C:29:94:B9:15' => {
280                 '10.0.0.128' => {   
281                     'tcp' => undef,
282                     'udp' => [ 23, 53, '80:1024', ],
283                 },
284             },
285         },
286     },
287 };
288
289 #my $hwall = getVar(name => "address");
290 #my $port = getVar(name => "port");
291
292 diag("Beginning our function testing now...");
293 $URL = HoneyClient::Manager::FW->init_fw();
294 is($URL, "http://192.168.0.129:8083/", "testing init_fw(), creation of the firewall server") or diag("Failed to start up the FW SOAP server.  Check to see if any other daemon is listening on TCP port $PORT.");
295 sleep 3;
296 is(HoneyClient::Manager::FW->destroy_fw(), 1, "destroy_fw(), destruction of the firewall server") or diag("Unable to terminate FW");
297 sleep 1;
298
299 =end testing
300
301 # This package name.
302 our $PACKAGE = __PACKAGE__;
303 our $DAEMON_PID : shared = undef;
304 # Complete URL of SOAP server, when initialized.
305 our $URL_BASE;
306 our $URL;
307 our $UPTIME = "/usr/bin/uptime";
308
309
310 BEGIN {
311
312     # Defines which functions can be called externally.
313     require Exporter;
314     our ( @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION );
315
316     # Set our package version.
317     $VERSION = 0.9;
318
319     @ISA = qw(Exporter);
320
321     # Symbols to export on request
322     @EXPORT =
323       qw( _parseHash _validateInit init_fw destroy_fw _doFullBackup _flushChains _setAcceptPolicy _setDefaultDeny _set_log_rules _setstaticrate _setDefaultRules _remoteConnection _set_ip_forwarding _getpid);
324
325     # Items to export into callers namespace by default. Note: do not export
326     # names by default without a very good reason. Use EXPORT_OK instead.
327     # Do not simply export all your public functions/methods/constants.
328
329     # This allows declaration use HoneyClient::Manager::FW ':all';
330     # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
331     # will save memory.
332
333     %EXPORT_TAGS = ( 'all' => [qw(init_fw destroy_fw)], );
334
335     # Symbols to autoexport (:DEFAULT tag)
336     @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
337
338     $SIG{PIPE} = 'IGNORE';    # Do not exit on broken pipes.
339 }
340
341
342 =pod
343
344 =head1 EXTERNAL SOAP FUNCTIONS
345
346 =over 4
347
348 =item *
349
350 fwInit()
351
352 The fwInit function awaits a call from the Honeyclient manager, once a call is made the function performs numerous subfunctions but
353 mainly handles creation of the default iptables ruleset for the honeyclient network.
354 IPTables ruleset:
355 Since we are using our honeywall to do transparent packet forwarding, forwarded packets will be traversing the
356 IPTables FORWARD chain, which is associated with the filter table.  By adding rules to the FORWARD chain, you
357 can control the flow of traffic between our two networks (honeyclient and external network). 
358 Instead of using a single, built-in chain for all protocols, we use a user-defined chain to determine the protocol type,
359 then hand off the actual final processing to our user-defined chain in the filter table.
360
361
362 I<Inputs>: nothing - No specific input
363 I<Output>: Success if default ruleset was created, failure if not
364
365 #=back
366
367 =begin testing
368
369 eval{
370     diag("Testing fwInit()...");
371     $URL = HoneyClient::Manager::FW->init_fw();
372     # Wait at least a second, in order to initialize the daemon.
373     sleep(1);
374     # Connect to daemon as a client.
375     $stub = getClientHandle(namespace => "HoneyClient::Manager::FW");
376     $som = $stub->fwInit($hashref);
377     $som = $stub->_validateInit();
378     is($som->result, 24, "fwInit current has set up 28 rules")   or diag("The fwInit() call failed.");
379     $som = $stub->_setAcceptPolicy();
380     $som = $stub->_flushChains();
381    
382 };
383
384 # Kill the child daemon, if it still exists.
385 HoneyClient::Manager::FW->destroy_fw();
386 sleep(1);
387
388 # Report any failure found.
389 if ($@) {
390     fail($@);
391 }
392    
393 =end testing
394
395 =cut
396
397 sub fwInit {
398     my ($class) = shift();
399     my ($systempid, $f_success, $del_success, $acceptsuccess, $denysuccess,
400         $chain)
401       = q{};
402     my @default_chains = qw(INPUT OUTPUT FORWARD);
403     my $outputdir      = getVar(name => "outputdir");
404     my $processname    = "startFWListener.pl";
405     my $table          = IPTables::IPv4::init('filter');
406
407
408     #$systempid = _getpid($processname);
409     my $log = get_logger("HoneyClient::Manager::FW");
410     $log->info("Entering fwInit(), starting Firewall initialization...");
411
412     # Could not connect to iptables
413     if (!defined($table)) {
414         $log->error_die("Error, could not connect to IPTABLES interface: $!");
415         return(0);
416     } else {
417         $log->info("table is defined");
418         # lets check for root access, the honeyclient can only be run as root
419         my $root = _checkRoot();
420         if (!$root) {
421             $log->error_die("Error, you must be root to run this program: $!");
422         }
423
424 # loads the interfaces in the /etc/interfaces.conf file - might be used later???
425 #   _load_interfaces();
426 # Peform a full backup of the existing rules
427         _doFullBackup($outputdir);
428
429 # set ip forwarding to 0.  The script initially turns all forwarding off while it loads the firewall policy.
430 # Then, right before the script exits, the script turns forwarding back on.
431         _set_ip_forwarding(0);
432
433         # Sets accept all policy, return $acceptsuccess code
434         _setAcceptPolicy();
435
436         # flush and delete all existing chains/rules - starting clean
437         _flushChains();
438        
439 # Now lets set our default deny all policy, this drops all traffic for INPUT, FORWARD, and OUTPUT
440         _setDefaultDeny();
441        
442
443         #  Create a Drop_Log_* user-defined chain, for logging packets before dropping them
444         _createDropLog("Drop_Log_In", "INPUT: ");
445         _createDropLog("Drop_Log_Out", "OUTPUT: ");
446         _createDropLog("Drop_Log_Fwd", "FORWARD: ");
447
448         # Now creating all rules for INPUT/OUTPUT/FORWARD chains
449         _setDefaultRules();
450
451 # Creates a nat rule in the POSTROUTING chain.  The nat POSTROUTING chain performs
452 # source network address translation and masquerading.  The chain can contain rules that modify
453 # the source IP address or source port of packets that traverse it.  We do not use this chain for
454 # any filtering.
455         _createNat();
456
457         # sets up ip forwarding to active
458         _set_ip_forwarding(1);
459         my $totalRules = _validateInit();
460         return $totalRules;
461     }
462 }    # end of setup subroutine
463
464 =pod
465
466 =item *
467
468 _validateInit() is another helper function
469
470 I<Inputs>:  no input
471 I<Output>: total number of rules across all chains (Default and user-defined)
472
473 =cut
474
475 sub _validateInit {
476     my $table     = IPTables::IPv4::init('filter');
477     my $natTable  = IPTables::IPv4::init('nat');
478     my @chainList = qw(INPUT OUTPUT FORWARD Drop_Log);
479     my @natChains = qw(PREROUTING POSTROUTING);
480     my (@ruleList, @natList) = ();
481     my ($numRules, $natRules, $totalRules) = q{};
482     foreach my $chain (@chainList) {
483         if (!$table->is_chain($chain)) {
484             my $filter = 0;
485         } else {
486             @ruleList = $table->list_rules($chain);
487             $numRules = scalar(@ruleList);
488             print "Number of rules in $chain:  $numRules\n";
489             $totalRules += $numRules;
490         }
491     }
492     foreach my $natChain (@natChains) {
493         if (!$natTable->is_chain($natChain)) {
494             my $nat = 0;
495         } else {
496             @natList  = $natTable->list_rules($natChain);
497             $natRules = scalar(@natList);
498             print "Number of rules in $natChain:  $natRules\n";
499             $totalRules += $natRules;
500         }
501     }
502     return $totalRules;
503 }
504
505 =pod
506
507 =item *
508
509 _parseHash() is another helper function that takes in a hash reference from the honeyclient
510 manager and parses the data structure thus producing usable values to generate our firewall rules.
511
512 I<Inputs>:  Requires hash reference (hohohohoh).
513 I<Output>: returns hash of a hash to be used during  the addRule() function for rule generation.
514
515 =cut
516
517 sub _parseHash {
518     my @temp = ();
519     my %HoH  = ();
520
521     # Extract arguments.
522     my ($hashref) = @_;
523
524     # Get the VM identifier.
525     foreach $vm_ID (keys %{$hashref}) {
526
527         # Get the VM's source MAC address.
528         foreach $src_MAC_addr (keys %{ $hashref->{$vm_ID}->{'sources'} }) {
529
530             # Get the VM's source IP address.
531             foreach $src_IP_addr (
532                      keys %{ $hashref->{$vm_ID}->{'sources'}->{$src_MAC_addr} })
533             {
534
535                 # Get the VM's source protocol.
536                 foreach $src_IP_proto (
537                             keys %{
538                                 $hashref->{$vm_ID}->{'sources'}->{$src_MAC_addr}
539                                   ->{$src_IP_addr}
540                             }
541                   )
542                 {
543
544           # Get the VM's source ports.
545           # For this case, we can't use foreach, since we have to also factor in
546           # cases where the array of ports is 'undef'.
547           # Get the list of ports.
548                     my @src_ports = ();
549                     if (
550                         defined(
551                                 $hashref->{$vm_ID}->{'sources'}->{$src_MAC_addr}
552                                   ->{$src_IP_addr}->{$src_IP_proto}
553                         )
554                       )
555                     {
556                         @src_ports =
557                           @{ $hashref->{$vm_ID}->{'sources'}->{$src_MAC_addr}
558                               ->{$src_IP_addr}->{$src_IP_proto} };
559                     }
560
561                     # Figure out how big the array is.
562                     my $num_of_src_ports = scalar(@src_ports);
563                     my $src_port_counter = 0;
564                     my $src_port         = undef;
565                     do {
566
567                         # We check to see if our source port array
568                         # is empty.
569                         if ($num_of_src_ports <= 0) {
570                             $src_port = "*";
571                         } else {
572                             $src_port = $src_ports[$src_port_counter];
573                         }
574
575                         # Get the target hosts.
576                         foreach $dst_host (
577                                       keys %{ $hashref->{$vm_ID}->{'targets'} })
578                         {
579
580                             # Get the target IPs.
581                             foreach $dst_IP_addr (_resolveHost($dst_host)) {
582
583                                 # Get the target protocol.
584                                 foreach $dst_IP_proto (
585                                              keys %{
586                                                  $hashref->{$vm_ID}->{'targets'}
587                                                    ->{$dst_host}
588                                              }
589                                   )
590                                 {
591
592          #print STDERR "Destination Protocol: " . $dst_IP_proto . "\n";
593          # We skip over combinations, where the source and destination protocols
594          # are different.
595                                     next
596                                       unless $src_IP_proto eq $dst_IP_proto;
597
598           # Get the target ports.
599           # For this case, we can't use foreach, since we have to also factor in
600           # cases where the array of ports is 'undef'.
601           # Get the list of ports.
602                                     my @dst_ports = ();
603                                     if (
604                                         defined(
605                                                 $hashref->{$vm_ID}->{'targets'}
606                                                   ->{$dst_host}->{$dst_IP_proto}
607                                         )
608                                       )
609                                     {
610                                         @dst_ports =
611                                           @{ $hashref->{$vm_ID}->{'targets'}
612                                               ->{$dst_host}->{$dst_IP_proto} };
613                                     }
614
615                                     # Figure out how big the array is.
616                                     my $num_of_dst_ports = scalar(@dst_ports);
617                                     my $dst_port_counter = 0;
618                                     my $dst_port         = undef;
619                                     do {
620
621                                  # We check to see if our destination port array
622                                  # is empty.
623                                         if ($num_of_dst_ports <= 0) {
624                                             $dst_port = "*";
625                                         } else {
626                                             $dst_port =
627                                               $dst_ports[$dst_port_counter];
628                                         }
629
630            # generate our rules here into a %HoH based on destination ip address
631                                         $HoH{$dst_IP_addr} = {
632                                             "chain"       => "$vm_ID",
633                                             "source-mac"  => "$src_MAC_addr",
634                                             "source"      => "$src_IP_addr",
635                                             "protocol"    => "$src_IP_proto",
636                                             "source-port" => "$src_port",
637                                             "destination-domain" => "$dst_host",
638                                             "destination" => "$dst_IP_addr",
639                                             "dest-proto"  => "$dst_IP_proto",
640                                             "destination-port" => "$dst_port",
641                                             "jump"             => "ACCEPT",
642                                             "matches"          => ['mac']
643                                         };
644                                         $dst_port_counter++;
645                                       } until (
646                                         $dst_port_counter >= $num_of_dst_ports);
647                                 }
648                             }
649                         }
650                         $src_port_counter++;
651                     } until ($src_port_counter >= $num_of_src_ports);
652                 }
653             }
654         }
655     }
656     return %HoH;
657 }
658
659 =pod
660
661 =item *
662
663 addChain();
664
665 add_vm_chain adds the user-defined chain based on manager client parameters.
666
667 I<Inputs>: 
668 B<$class>is the package name
669 B<$hashref> - hash reference that will be sent over from HoneyClient::Agent.  It will then be parsed by get_vm_from_hash()
670 and give me an array of VM names that will be added from the iptables chain list.  The reason we have broken
671 add_vm_chain() up and made it its separate subroutine is because when we add a rule to the iptables ruleset, we
672 must first have a user-defined chain in place.  A commit must occur which writes it to the kernel-level netfilter subsystem, then
673 the rule must be added after that has occurred.
674
675 I<Output>: returns true if VM chain was deleted, returns false if not
676
677 #=back
678
679 =begin testing
680
681 eval{
682     diag("Testing addChain()...");
683     $URL = HoneyClient::Manager::FW->init_fw();
684     # Wait at least a second, in order to initialize the daemon.
685     sleep 1;
686     # Connect to daemon as a client.
687     $stub = getClientHandle(namespace => "HoneyClient::Manager::FW");
688     $som = $stub->addChain($hashref);
689     ok($som->result, "addChain() successfully passed.")   or diag("The addChain() call failed.");
690     $som = $stub->_setAcceptPolicy();
691     $som = $stub->_flushChains();
692 };
693
694 # Kill the child daemon, if it still exists.
695 HoneyClient::Manager::FW->destroy_fw();
696 sleep 1;
697
698 # Report any failure found.
699 if ($@) {
700     fail($@);
701     }
702    
703 =end testing
704
705 =cut
706
707 sub addChain {
708     my ($class) = shift;    # pass in package name
709     my $hashref =
710       shift;    # pass in hash reference which is sent from HoneyClient::Agent
711     my $log   = get_logger("HoneyClient::Manager::FW");
712     my $table = IPTables::IPv4::init('filter');
713     my ($vmlistin, $vmlistout, $vinret, $voutret) = q{};
714
715 # get_vm_from_hash() returns the VM name of the hash reference.  For now, one VM md5sum value
716 # will be passed in one hashref.
717     $vmname = _getVMName($hashref);
718
719     # lets modify the vmnames to apply to "-IN" and "-OUT" flows
720     my $vin  = $vmname . "-IN";
721     my $vout = $vmname . "-OUT";
722
723   # Lets loop through the array contain all "-in" VM chain names and create them
724     if ($table->is_chain($vin)) {
725         $log->info("Sorry, $vin already exists - no chain was created");
726         $vinret = 0;
727     } else {
728         $log->info("$vin was not found and created");
729         $table->create_chain($vin) or
730             die ("Error: Unable to create chain $vin");
731
732         # the entry will be inserted to the head of the FORWARD chain (0)
733         $table->insert_entry("FORWARD", { 'protocol' => "tcp", jump => $vin },
734                              0) or
735             die ("Error: Unable to insert entry into chain FORWARD");
736         $log->info("Inserting rule in FORWARD chain to point to $vin");
737         $vinret = 1;
738     }
739
740  # Lets loop through the array contain all "-out" VM chain names and create them
741     if ($table->is_chain($vout)) {
742         $log->info("Sorry, $vout already exists and was not created");
743         $voutret = 0;
744     } else {
745         $log->info("$vout chain was was not found and created");
746         $table->create_chain($vout) or
747             die ("Error: Unable to create chain $vout");
748
749         # the entry will be inserted to the head of the FORWARD chain (0)
750         $table->insert_entry("FORWARD", { 'protocol' => "tcp", jump => $vout },
751                              0) or
752             die ("Error: Unable to insert entry into chain FORWARD");
753         $voutret = 1;
754     }
755
756     # write to the iptables ruleset
757     $log->info("Commiting all rules in addChain() function");
758     $table->commit() or die ("Error: Unable to commit changes to filter table");
759     return ($vinret, $voutret);
760 }
761
762 =pod
763
764 =item *
765
766 deleteChain();
767
768 delete_vm_chain deletes the user-defined chain based on manager client parameters.
769
770 I<Inputs>: 
771 B<$class>is the package name
772 B<$hashref> - hash reference that will be sent over from HoneyClient::Agent.  It will then be parsed by get_vm_from_hash()
773 and give me an array of VM names that will be deleted from the iptables chain list.
774
775 I<Output>: returns true if VM chain was deleted, returns false if not
776
777 =begin testing
778
779 eval{
780      diag("Testing deleteChain()...");
781     $URL = HoneyClient::Manager::FW->init_fw();
782     # Wait at least a second, in order to initialize the daemon.
783     sleep 1;
784     # Connect to daemon as a client.
785     $stub = getClientHandle(namespace => "HoneyClient::Manager::FW");
786     $som = $stub->addChain($hashref);
787     sleep 1;
788     $som = $stub->deleteChain($hashref);
789     ok($som->result, "deleteChain() successfully passed.")   or diag("The deleteChain() call failed.");
790     $som = $stub->_setAcceptPolicy();
791     $som = $stub->_flushChains();
792
793 };
794
795 # Kill the child daemon, if it still exists.
796 HoneyClient::Manager::FW->destroy_fw();
797 sleep 1;
798
799 # Report any failure found.
800 if ($@) {
801     fail($@);
802     }
803    
804 =end testing
805
806 =cut
807
808 sub deleteChain {
809     my ($class)      = shift;
810     my ($hashref)    = shift;
811     my $table        = IPTables::IPv4::init('filter');
812     my @forwardrules = $table->list_rules("FORWARD");
813     my $vmname       = _getVMName($hashref);
814     my @chainArray   = ();
815     my $log   = get_logger("HoneyClient::Manager::FW");
816     $log->info("Entering deleteChain()...");
817
818     # concatenate vm names to specify in and out chains
819     my $vin  = $vmname . "-IN";
820     my $vout = $vmname . "-OUT";
821     push(@chainArray, $vin);
822     push(@chainArray, $vout);
823
824 # Within this loop, we are deleting the rules within the FORWARD chain, since our chain is
825 # user-defined, we also has a rule within the FORWARD chain that points to the next chain but
826 # we need to delete this rule too.  This loop applies the user-defined "in" chain.
827     $log->info("Starting to loop all the rules in the FORWARD chain");
828     for (my $i = 0 ; $i <= $#forwardrules ; $i++) {
829
830         # if there is a match, delete it
831         if (   ($forwardrules[$i]->{'protocol'} eq "tcp")
832             && ($forwardrules[$i]->{'jump'} eq $vin))
833         {
834             $log->info("Deleting rule in FORWARD chain where jump is $vin");
835             $table->delete_entry("FORWARD", $forwardrules[$i]) or
836                 die ("Error: Unable to delete entry in chain FORWARD");
837             print "deleting $forwardrules[$i] in FORWARD chain\n";
838         } else {
839             print "No match with the rules, keep looking\n";
840         }
841     }
842
843 # Within this loop, we are deleting the rules within the FORWARD chain, since our chain is
844 # user-defined, we also has a rule within the FORWARD chain that points to the next chain but
845 # we need to delete this rule too.  This loop applies the user-defined "out" chain.
846     for (my $j = 0 ; $j <= $#forwardrules ; $j++) {
847         if (   ($forwardrules[$j]->{'protocol'} eq "tcp")
848             && ($forwardrules[$j]->{'jump'} eq $vout))
849         {
850             $log->info("Deleting rule in FORWARD chain where jump is $vout");
851             $table->delete_entry("FORWARD", $forwardrules[$j]) or
852                 die ("Error: Unable to delete entry in chain FORWARD");
853         } else {
854             print "No match with the rules, keep looking\n";
855         }
856     }
857
858 # Flush the entry and delete the chain here
859 # This deletes all the rules within that chain first, then deletes the actual chain last.
860 $log->info("Flushing the entries and chains now...");
861     foreach my $chainname (@chainArray) {
862         $log->info("Flusing entries in $chainname");
863         $table->flush_entries($chainname) or
864             die ("Error: Unable to flush entries in chain $chainname");
865         $log->info("Deleting $chainname");
866         $table->delete_chain($chainname);
867             die ("Error: Unable to delete chain $chainname");
868     }
869     $table->commit() or die ("Error: Unable to commit changes to filter table");
870 }
871
872 #Deletes the rules that it would have added for a given hashref in a given custom chain
873 sub deleteRules(){
874 my ($class)      = shift;
875 my ($hashref)    = shift;
876 my $vmname       = _getVMName($hashref);
877 my @chainArray   = ();
878 my $table        = IPTables::IPv4::init('filter');
879 my $result       = 0;
880
881 #   print Dumper($table)
882     # concatenating chain name to handle "in" chain flow and "out" chain flow
883     $vin  = $vmname . "-IN";
884     $vout = $vmname . "-OUT";
885
886     $result = $table->flush_entries($vin);
887     if($result){
888         print "flushed entries from $vin\n"
889     }
890     else{
891         die ("Error: Unable to flush entries in chain $vin");
892         print "flush on $vin failed\n";
893         return $result;
894     }
895    
896     $result = $table->flush_entries($vout);
897     if($result){
898         print "flushed entries from $vout\n";
899     }
900     else{
901         die ("Error: Unable to flush entries in chain $vout");
902         print "flush on $vout failed\n";
903         return $result;
904     }
905    
906     $table->commit() or die ("Error: Unable to commit changes to filter table");
907     # Lets delete our PREROUTING logging
908     $result = _deletePreroutingLogging($hashref, $vmname);
909     print "got $result in deleteRules\n";
910     return $result;
911 }
912
913 =pod
914
915 =item *
916
917 addRule($hashref)
918
919 addRule is a function that handles the addition of a new iptable rule into the existing IPTables ruleset which allow honeyclients functionality to crawl
920 the internet in search of malicious web sites.  FWPunch first checks for the existance of the user-defined chain before it
921 creates a new VM rule.  If the chain already exists, the rule can not be added since their is no corresponding chain.
922 If it does exist, the rule is added successfully.  All FWPunch calls are logged.
923
924 The addRule() function will recieve a $hashref which will be a muli-level hash table whose structure
925 will resemble the below data structure:
926
927 my $hashref = {
928
929     '0d599f0ec05c3bda8c3b8a6' => {    # VM identifier.
930         # You'll notice that we add a new layer to the table.
931         # This is an MD5SUM that represents the unique identifier
932         # of the VM *NAME*.  You can assume that this name will be
933         # initially generated and used by HC::Manager::VM.
934
935         # The next 2 keys contain the data from HC::Agent::Driver->next()
936         'targets' => {    # This keyname used to be called 'servers'
937
938             # The browser will contact 'www.mitre.org' at
939             # TCP port 80.
940             'www.mitre.org'   => { 'tcp' => [ 80 ], },
941             'rcf.mitre.org'   => { 'tcp' => [ 80 ], },
942             'blogs.mitre.org' => { 'tcp' => [ 80 ], },
943             'www.cnn.com'     => { 'tcp' => [ 80, 8080 ], },
944             'pool.ntp.org'    => { 'udp' => [ 123, ], },
945
946             # General example:
947             # The application will contact '192.168.1.1' at
948             # some unknown TCP port.
949             #
950             # If the ports are unknown, the firewall
951             # should allow outbound contact to all unprivileged
952             # ports (1025-65535) on the specified server. (Yes, I
953             # realize in reality, there's an upstream DMZ firewall
954             # blocking this functionality; this is simply an
955             # architectural design, for now.)
956             #'192.168.1.1' => { 'tcp' => "undef", },
957
958             # Or, more generically:
959             #'hostname_or_IP' => {
960             #    'protocol_type' => [ portnumbers_as_list ],
961             #},
962
963         },
964
965         'resources' => {
966
967             # This hashtable contains a list of key, value pairs
968             # that reflect the list of resources that the driver
969             # will contact next. For browsers, this means URLs.
970             # The (1) value is simply a numerical
971             # placeholder that serves no purpose, for now.
972             'http://www.mitre.org' => 1,
973         },
974
975         # Now, we expect HC::Manager to add this next key.
976         # This sub-hashtable contains all the source IPs, ports,
977         # and protocols that the VM will use to make its outbound
978         # connection to remote resources.
979         'sources' => {
980
981             '00:04:23:08:7d:54' => { # The VM's MAC address.
982
983                 '10.0.0.128' => {    # The VM's internal IP.
984
985                     # In most cases, a VM will only _have_ one
986                     # NAT-ted IP.  However, this format allows for the
987                     # remote possibility that a VM may have multiple
988                     # virtual NICs or multiple IP aliases, in which case
989                     # the VM may have multiple source IPs.
990
991                     'tcp' => undef,
992
993                     # We use the same protocol => port syntax as described
994                     # before.  Again, if the value is 'undef', then that
995                     # indicates that the VM may use any unprivileged port
996                     # locally inside its OS, when initiating outbound
997                     # communication to the remote resource.
998
999                     'udp' => [ 23, 53, '80:1024', ],
1000                 },
1001             },
1002         },
1003     },
1004 };
1005
1006 I<Inputs>:
1007 B<$class> is the package name.
1008 B<$hashref>
1009
1010 I<Output>: Return true if success or false if failure
1011
1012 =back
1013
1014 =begin testing
1015
1016 eval{
1017      diag("Testing addRule()...");
1018     $URL = HoneyClient::Manager::FW->init_fw();
1019     # Wait at least a second, in order to initialize the daemon.
1020     sleep 1;
1021     # Connect to daemon as a client.
1022     $stub = getClientHandle(namespace => "HoneyClient::Manager::FW");
1023     my $som  = $stub->fwInit($hashref);
1024     $som = $stub->addChain($hashref);
1025     $som = $stub->addRule($hashref);
1026     ok($som->result, "addRule() successfully passed and added a new rule.")   or diag("The addRule() call failed.");
1027     $som = $stub->_setAcceptPolicy();
1028     $som = $stub->_flushChains();
1029 };
1030
1031 # Kill the child daemon, if it still exists.
1032 HoneyClient::Manager::FW->destroy_fw();
1033 sleep 1;
1034
1035 # Report any failure found.
1036 if ($@) {
1037     fail($@);
1038     }
1039
1040 =end testing
1041
1042 =cut
1043
1044 sub addRules {
1045     use Time::HiRes qw(gettimeofday);
1046
1047     #   my ( $class, $dest, $port, $source, $protocol, $vmnum ) = @_;
1048     my ($class)   = shift;
1049     my ($hashref) = shift;
1050     my $table     = IPTables::IPv4::init('filter');
1051     my ($log, $vin, $vout) = q{};
1052     my $counter = 0;
1053     my %rules   = ();
1054
1055  #start represents the time at which we starting to time our addChain() function
1056     my $start = gettimeofday();
1057
1058     # debugging options, automatically logs to logfile for review later
1059     $log =