root/honeyclient/branches/rel/1.0/lib/HoneyClient/Manager/FW.pm

Revision 994, 96.4 kB (checked in by kindlund, 1 year ago)

Bumped version to v1.00.

  • 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) 2007 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.00.
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 = 1.00;
318
319     @ISA = qw(Exporter);
320
321     # Symbols to export automatically
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 (when qw(:all) tag is used)
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 # TODO: Uncomment and resolve this, eventually.
428 # It has been disabled for now, since it was filling up system space on the
429 # Honeywall VM.
430 #       _doFullBackup($outputdir);
431
432 # set ip forwarding to 0.  The script initially turns all forwarding off while it loads the firewall policy.
433 # Then, right before the script exits, the script turns forwarding back on.
434         _set_ip_forwarding(0);
435
436         # Sets accept all policy, return $acceptsuccess code
437         _setAcceptPolicy();
438
439         # flush and delete all existing chains/rules - starting clean
440         _flushChains();
441
442 # Now lets set our default deny all policy, this drops all traffic for INPUT, FORWARD, and OUTPUT
443         _setDefaultDeny();
444
445
446         #  Create a Drop_Log_* user-defined chain, for logging packets before dropping them
447         _createDropLog("Drop_Log_In", "INPUT: ");
448         _createDropLog("Drop_Log_Out", "OUTPUT: ");
449         _createDropLog("Drop_Log_Fwd", "FORWARD: ");
450
451         # Now creating all rules for INPUT/OUTPUT/FORWARD chains
452         _setDefaultRules();
453
454 # Creates a nat rule in the POSTROUTING chain.  The nat POSTROUTING chain performs
455 # source network address translation and masquerading.  The chain can contain rules that modify
456 # the source IP address or source port of packets that traverse it.  We do not use this chain for
457 # any filtering.
458         _createNat();
459
460         # sets up ip forwarding to active
461         _set_ip_forwarding(1);
462         my $totalRules = _validateInit();
463         return $totalRules;
464     }
465 }    # end of setup subroutine
466
467 =pod
468
469 =item *
470
471 _validateInit() is another helper function
472
473 I<Inputs>:  no input
474 I<Output>: total number of rules across all chains (Default and user-defined)
475
476 =cut
477
478 sub _validateInit {
479     my $table     = IPTables::IPv4::init('filter');
480     my $natTable  = IPTables::IPv4::init('nat');
481     my @chainList = qw(INPUT OUTPUT FORWARD Drop_Log);
482     my @natChains = qw(PREROUTING POSTROUTING);
483     my (@ruleList, @natList) = ();
484     my ($numRules, $natRules, $totalRules) = q{};
485     foreach my $chain (@chainList) {
486         if (!$table->is_chain($chain)) {
487             my $filter = 0;
488         } else {
489             @ruleList = $table->list_rules($chain);
490             $numRules = scalar(@ruleList);
491             print "Number of rules in $chain:  $numRules\n";
492             $totalRules += $numRules;
493         }
494     }
495     foreach my $natChain (@natChains) {
496         if (!$natTable->is_chain($natChain)) {
497             my $nat = 0;
498         } else {
499             @natList  = $natTable->list_rules($natChain);
500             $natRules = scalar(@natList);
501             print "Number of rules in $natChain:  $natRules\n";
502             $totalRules += $natRules;
503         }
504     }
505     return $totalRules;
506 }
507
508 =pod
509
510 =item *
511
512 _parseHash() is another helper function that takes in a hash reference from the honeyclient
513 manager and parses the data structure thus producing usable values to generate our firewall rules.
514
515 I<Inputs>:  Requires hash reference (hohohohoh).
516 I<Output>: returns hash of a hash to be used during  the addRule() function for rule generation.
517
518 =cut
519
520 sub _parseHash {
521     my @temp = ();
522     my %HoH  = ();
523
524     # Extract arguments.
525     my ($hashref) = @_;
526
527     # Get the VM identifier.
528     foreach $vm_ID (keys %{$hashref}) {
529
530         # Get the VM's source MAC address.
531         foreach $src_MAC_addr (keys %{ $hashref->{$vm_ID}->{'sources'} }) {
532
533             # Get the VM's source IP address.
534             foreach $src_IP_addr (
535                      keys %{ $hashref->{$vm_ID}->{'sources'}->{$src_MAC_addr} })
536             {
537
538                 # Get the VM's source protocol.
539                 foreach $src_IP_proto (
540                             keys %{
541                                 $hashref->{$vm_ID}->{'sources'}->{$src_MAC_addr}
542                                   ->{$src_IP_addr}
543                             }
544                   )
545                 {
546
547           # Get the VM's source ports.
548           # For this case, we can't use foreach, since we have to also factor in
549           # cases where the array of ports is 'undef'.
550           # Get the list of ports.
551                     my @src_ports = ();
552                     if (
553                         defined(
554                                 $hashref->{$vm_ID}->{'sources'}->{$src_MAC_addr}
555                                   ->{$src_IP_addr}->{$src_IP_proto}
556                         )
557                       )
558                     {
559                         @src_ports =
560                           @{ $hashref->{$vm_ID}->{'sources'}->{$src_MAC_addr}
561                               ->{$src_IP_addr}->{$src_IP_proto} };
562                     }
563
564                     # Figure out how big the array is.
565                     my $num_of_src_ports = scalar(@src_ports);
566                     my $src_port_counter = 0;
567                     my $src_port         = undef;
568                     do {
569
570                         # We check to see if our source port array
571                         # is empty.
572                         if ($num_of_src_ports <= 0) {
573                             $src_port = "*";
574                         } else {
575                             $src_port = $src_ports[$src_port_counter];
576                         }
577
578                         # Get the target hosts.
579                         foreach $dst_host (
580                                       keys %{ $hashref->{$vm_ID}->{'targets'} })
581                         {
582
583                             # Get the target IPs.
584                             foreach $dst_IP_addr (_resolveHost($dst_host)) {
585
586                                 # Get the target protocol.
587                                 foreach $dst_IP_proto (
588                                              keys %{
589                                                  $hashref->{$vm_ID}->{'targets'}
590                                                    ->{$dst_host}
591                                              }
592                                   )
593                                 {
594
595          #print STDERR "Destination Protocol: " . $dst_IP_proto . "\n";
596          # We skip over combinations, where the source and destination protocols
597          # are different.
598                                     next
599                                       unless $src_IP_proto eq $dst_IP_proto;
600
601           # Get the target ports.
602           # For this case, we can't use foreach, since we have to also factor in
603           # cases where the array of ports is 'undef'.
604           # Get the list of ports.
605                                     my @dst_ports = ();
606                                     if (
607                                         defined(
608                                                 $hashref->{$vm_ID}->{'targets'}
609                                                   ->{$dst_host}->{$dst_IP_proto}
610                                         )
611                                       )
612                                     {
613                                         @dst_ports =
614                                           @{ $hashref->{$vm_ID}->{'targets'}
615                                               ->{$dst_host}->{$dst_IP_proto} };
616                                     }
617
618                                     # Figure out how big the array is.
619                                     my $num_of_dst_ports = scalar(@dst_ports);
620                                     my $dst_port_counter = 0;
621                                     my $dst_port         = undef;
622                                     do {
623
624                                  # We check to see if our destination port array
625                                  # is empty.
626                                         if ($num_of_dst_ports <= 0) {
627                                             $dst_port = "*";
628                                         } else {
629                                             $dst_port =
630                                               $dst_ports[$dst_port_counter];
631                                         }
632
633            # generate our rules here into a %HoH based on destination ip address
634                                         $HoH{$dst_IP_addr} = {
635                                             "chain"       => "$vm_ID",
636                                             "source-mac"  => "$src_MAC_addr",
637                                             "source"      => "$src_IP_addr",
638                                             "protocol"    => "$src_IP_proto",
639                                             "source-port" => "$src_port",
640                                             "destination-domain" => "$dst_host",
641                                             "destination" => "$dst_IP_addr",
642                                             "dest-proto"  => "$dst_IP_proto",
643                                             "destination-port" => "$dst_port",
644                                             "jump"             => "ACCEPT",
645                                             "matches"          => ['mac']
646                                         };
647                                         $dst_port_counter++;
648                                       } until (
649                                         $dst_port_counter >= $num_of_dst_ports);
650                                 }
651                             }
652                         }
653                         $src_port_counter++;
654                     } until ($src_port_counter >= $num_of_src_ports);
655                 }
656             }
657         }
658     }
659     return %HoH;
660 }
661
662 =pod
663
664 =item *
665
666 addChain();
667
668 add_vm_chain adds the user-defined chain based on manager client parameters.
669
670 I<Inputs>:
671 B<$class>is the package name
672 B<$hashref> - hash reference that will be sent over from HoneyClient::Agent.  It will then be parsed by get_vm_from_hash()
673 and give me an array of VM names that will be added from the iptables chain list.  The reason we have broken
674 add_vm_chain() up and made it its separate subroutine is because when we add a rule to the iptables ruleset, we
675 must first have a user-defined chain in place.  A commit must occur which writes it to the kernel-level netfilter subsystem, then
676 the rule must be added after that has occurred.
677
678 I<Output>: returns true if VM chain was deleted, returns false if not
679
680 #=back
681
682 =begin testing
683
684 eval{
685     diag("Testing addChain()...");
686     $URL = HoneyClient::Manager::FW->init_fw();
687     # Wait at least a second, in order to initialize the daemon.
688     sleep 1;
689     # Connect to daemon as a client.
690     $stub = getClientHandle(namespace => "HoneyClient::Manager::FW");
691     $som = $stub->addChain($hashref);
692     ok($som->result, "addChain() successfully passed.")   or diag("The addChain() call failed.");
693     $som = $stub->_setAcceptPolicy();
694     $som = $stub->_flushChains();
695 };
696
697 # Kill the child daemon, if it still exists.
698 HoneyClient::Manager::FW->destroy_fw();
699 sleep 1;
700
701 # Report any failure found.
702 if ($@) {
703     fail($@);
704     }
705
706 =end testing
707
708 =cut
709
710 sub addChain {
711     my ($class) = shift;    # pass in package name
712     my $hashref =
713       shift;    # pass in hash reference which is sent from HoneyClient::Agent
714     my $log   = get_logger("HoneyClient::Manager::FW");
715     my $table = IPTables::IPv4::init('filter');
716     my ($vmlistin, $vmlistout, $vinret, $voutret) = q{};
717
718 # get_vm_from_hash() returns the VM name of the hash reference.  For now, one VM md5sum value
719 # will be passed in one hashref.
720     $vmname = _getVMName($hashref);
721
722     # lets modify the vmnames to apply to "-IN" and "-OUT" flows
723     my $vin  = $vmname . "-IN";
724     my $vout = $vmname . "-OUT";
725
726   # Lets loop through the array contain all "-in" VM chain names and create them
727     if ($table->is_chain($vin)) {
728         $log->info("Sorry, $vin already exists - no chain was created");
729         $vinret = 0;
730     } else {
731         $log->info("$vin was not found and created");
732         $table->create_chain($vin) or
733             die ("Error: Unable to create chain $vin");
734
735         # the entry will be inserted to the head of the FORWARD chain (0)
736         $table->insert_entry("FORWARD", { 'protocol' => "tcp", jump => $vin },
737                              0) or
738             die ("Error: Unable to insert entry into chain FORWARD");
739         $log->info("Inserting rule in FORWARD chain to point to $vin");
740         $vinret = 1;
741     }
742
743  # Lets loop through the array contain all "-out" VM chain names and create them
744     if ($table->is_chain($vout)) {
745         $log->info("Sorry, $vout already exists and was not created");
746         $voutret = 0;
747     } else {
748         $log->info("$vout chain was was not found and created");
749         $table->create_chain($vout) or
750             die ("Error: Unable to create chain $vout");
751
752         # the entry will be inserted to the head of the FORWARD chain (0)
753         $table->insert_entry("FORWARD", { 'protocol' => "tcp", jump => $vout },
754                              0) or
755             die ("Error: Unable to insert entry into chain FORWARD");
756         $voutret = 1;
757     }
758
759     # write to the iptables ruleset
760     $log->info("Commiting all rules in addChain() function");
761     $table->commit() or die ("Error: Unable to commit changes to filter table");
762     return ($vinret, $voutret);
763 }
764
765 =pod
766
767 =item *
768
769 deleteChain();
770
771 delete_vm_chain deletes the user-defined chain based on manager client parameters.
772
773 I<Inputs>:
774 B<$class>is the package name
775 B<$hashref> - hash reference that will be sent over from HoneyClient::Agent.  It will then be parsed by get_vm_from_hash()
776 and give me an array of VM names that will be deleted from the iptables chain list.
777
778 I<Output>: returns true if VM chain was deleted, returns false if not
779
780 =begin testing
781
782 eval{
783      diag("Testing deleteChain()...");
784     $URL = HoneyClient::Manager::FW->init_fw();
785     # Wait at least a second, in order to initialize the daemon.
786     sleep 1;
787     # Connect to daemon as a client.
788     $stub = getClientHandle(namespace => "HoneyClient::Manager::FW");
789     $som = $stub->addChain($hashref);
790     sleep 1;
791     $som = $stub->deleteChain($hashref);
792     ok($som->result, "deleteChain() successfully passed.")   or diag("The deleteChain() call failed.");
793     $som = $stub->_setAcceptPolicy();
794     $som = $stub->_flushChains();
795
796 };
797
798 # Kill the child daemon, if it still exists.
799 HoneyClient::Manager::FW->destroy_fw();
800 sleep 1;
801
802 # Report any failure found.
803 if ($@) {
804     fail($@);
805     }
806
807 =end testing
808
809 =cut
810
811 sub deleteChain {
812     my ($class)      = shift;
813     my ($hashref)    = shift;
814     my $table        = IPTables::IPv4::init('filter');
815     my @forwardrules = $table->list_rules("FORWARD");
816     my $vmname       = _getVMName($hashref);
817     my @chainArray   = ();
818     my $log   = get_logger("HoneyClient::Manager::FW");
819     $log->info("Entering deleteChain()...");
820
821     # concatenate vm names to specify in and out chains
822     my $vin  = $vmname . "-IN";
823     my $vout = $vmname . "-OUT";
824     push(@chainArray, $vin);
825     push(@chainArray, $vout);
826
827 # Within this loop, we are deleting the rules within the FORWARD chain, since our chain is
828 # user-defined, we also has a rule within the FORWARD chain that points to the next chain but
829 # we need to delete this rule too.  This loop applies the user-defined "in" chain.
830     $log->info("Starting to loop all the rules in the FORWARD chain");
831     for (my $i = 0 ; $i <= $#forwardrules ; $i++) {
832
833         # if there is a match, delete it
834         if (   ($forwardrules[$i]->{'protocol'} eq "tcp")
835             && ($forwardrules[$i]->{'jump'} eq $vin))
836         {
837             $log->info("Deleting rule in FORWARD chain where jump is $vin");
838             $table->delete_entry("FORWARD", $forwardrules[$i]) or
839                 die ("Error: Unable to delete entry in chain FORWARD");
840             print "deleting $forwardrules[$i] in FORWARD chain\n";
841         } else {
842             print "No match with the rules, keep looking\n";
843         }
844     }
845
846 # Within this loop, we are deleting the rules within the FORWARD chain, since our chain is
847 # user-defined, we also has a rule within the FORWARD chain that points to the next chain but
848 # we need to delete this rule too.  This loop applies the user-defined "out" chain.
849     for (my $j = 0 ; $j <= $#forwardrules ; $j++) {
850         if (   ($forwardrules[$j]->{'protocol'} eq "tcp")
851             && ($forwardrules[$j]->{'jump'} eq $vout))
852         {
853             $log->info("Deleting rule in FORWARD chain where jump is $vout");
854             $table->delete_entry("FORWARD", $forwardrules[$j]) or
855                 die ("Error: Unable to delete entry in chain FORWARD");
856         } else {
857             print "No match with the rules, keep looking\n";
858         }
859     }
860
861 # Flush the entry and delete the chain here
862 # This deletes all the rules within that chain first, then deletes the actual chain last.
863 $log->info("Flushing the entries and chains now...");
864     foreach my $chainname (@chainArray) {
865         $log->info("Flusing entries in $chainname");
866         $table->flush_entries($chainname) or
867             die ("Error: Unable to flush entries in chain $chainname");
868         $log->info("Deleting $chainname");
869         $table->delete_chain($chainname);
870             die ("Error: Unable to delete chain $chainname");
871     }
872     $table->commit() or die ("Error: Unable to commit changes to filter table");
873 }
874
875 #Deletes the rules that it would have added for a given hashref in a given custom chain
876 sub deleteRules(){
877 my ($class)      = shift;
878 my ($hashref)    = shift;
879 my $vmname       = _getVMName($hashref);
880 my @chainArray   = ();
881 my $table        = IPTables::IPv4::init('filter');