File Coverage

blib/lib/PIX/Accesslist.pm
Criterion Covered Total %
statement 12 132 9.0
branch 0 38 0.0
condition 0 26 0.0
subroutine 4 19 21.0
pod 4 5 80.0
total 20 220 9.0


line stmt bran cond sub pod time code
1             package PIX::Accesslist;
2              
3 1     1   7 use strict;
  1         1  
  1         36  
4 1     1   6 use warnings;
  1         2  
  1         25  
5              
6 1     1   5 use Carp;
  1         3  
  1         60  
7 1     1   600 use PIX::Accesslist::Line;
  1         2  
  1         1898  
8              
9             our $VERSION = '1.10';
10              
11             =pod
12              
13             =head1 NAME
14              
15             PIX::Accesslist - Accesslist object for use with PIX::Walker
16              
17             =head1 SYNOPSIS
18              
19             PIX::Accesslist is used by PIX::Walker to hold an ACL from a PIX firewall.
20             This allows you to programmatically step through an ACL and match lines
21             to certain criteria.
22              
23             See B for an example.
24              
25             $acl = new PIX::Accesslist($name, $acl_conf, $walker);
26              
27              
28             =head1 METHODS
29              
30             =over
31              
32             =cut
33              
34             sub new {
35 0     0 0   my $proto = shift;
36 0   0       my $class = ref($proto) || $proto;
37 0           my $self = { };
38 0           my ($name, $config, $walker) = @_;
39 0 0 0       croak("Must provide the access-list name and config block") unless ($name and $config);
40              
41 0           $self->{class} = $class;
42 0           $self->{name} = $name;
43 0           $self->{config} = [ @$config ];
44 0           $self->{config_block} = [ @$config ];
45 0           $self->{walker} = $walker;
46 0           $self->{acl} = [];
47 0           $self->{linenum} = 0;
48              
49 0           bless($self, $class);
50 0           $self->_init;
51              
52 0           return $self;
53             }
54              
55             sub _init {
56 0     0     my $self = shift;
57              
58 0 0         if (@{$self->{config_block}}[0] !~ /^access-list \S+ \S+/i) {
  0            
59 0           carp("Invalid config block passed to $self->{class}");
60 0           return undef;
61             }
62              
63 0           $self->{unnamed_any} = 0;
64 0           $self->{unnamed_host} = 0;
65 0           $self->{unnamed_net} = 0;
66 0           $self->{unnamed_proto} = 0;
67 0           $self->{unnamed_service} = 0;
68              
69 0           my $idx = 0;
70 0           my ($word, $next);
71 0           while (defined(my $line = $self->_nextline)) {
72 0           $idx++;
73 0           $line =~ s/^access-list $self->{name}(?: extended)? //;
74 0 0         next if $line =~ /^remark/; # ignore remarks
75 0 0         next unless $line =~ s/^(permit|deny)//; # strip off action
76              
77 0           my $action = lc $1;
78 0           my $proto = $self->_getproto(\$line);
79 0           my $source = $self->_getnetwork(\$line);
80 0           my $sport = $self->_getports(\$line, $proto); # will be undef if there's no port or service object-group
81 0           my $dest = $self->_getnetwork(\$line);
82 0           my $dport = $self->_getports(\$line, $proto); # ...
83              
84 0           my $o = new PIX::Accesslist::Line($action, $proto, $source, $sport, $dest, $dport, $idx, $self);
85 0           push(@{$self->{acl}}, $o);
  0            
86             }
87             }
88              
89             # returns the next network from the current line. $line is a ref
90             sub _getnetwork {
91 0     0     my ($self, $line) = @_;
92 0 0         croak("\$line must be a reference") unless ref $line;
93 0           my $net;
94              
95 0           my $word = $self->_nextword($line);
96              
97             # ignore the 'inferface' source if specified, it does us no good
98             #if ($word eq 'interface') {
99             # print "$$line\n";
100             # $word = $self->_nextword($line); # ignore the interface name
101             # $word = $self->_nextword($line); # get the next word which should actually be something we expect below
102             #}
103              
104 0 0 0       if ($word eq 'object-group') {
    0          
    0          
    0          
105 0           $net = $self->{walker}->obj( $self->_nextword($line) );
106              
107             } elsif ($word eq 'any') {
108 0           my $name = 'unnamed_any_'.(++$self->{unnamed_any});
109 0           my $conf = [
110             "object-group network $name",
111             "network-object 0.0.0.0 0.0.0.0"
112             ];
113 0           $net = new PIX::Object('network', $name, $conf, $self->{walker});
114              
115             } elsif ($word eq 'host') {
116 0           my $ip = $self->_nextword($line);
117 0           my $name = 'unnamed_host_'.(++$self->{unnamed_host});
118 0           my $conf = [
119             "object-group network $name",
120             "network-object host " . $self->{walker}->alias($ip)
121             ];
122 0           $net = new PIX::Object('network', $name, $conf, $self->{walker});
123              
124             } elsif (($word =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) || ($word ne $self->{walker}->alias($word))) {
125 0           my $name = 'unnamed_net_'.(++$self->{unnamed_net});
126 0           my $conf = [
127             "object-group network $name",
128             "network-object " . $self->{walker}->alias($word) . " " . $self->_nextword($line)
129             ];
130 0           $net = new PIX::Object('network', $name, $conf, $self->{walker});
131              
132             } else {
133 0           warn "** Unknown network: '$word' at '$self->{name}' line $self->{linenum}: $$line\n";
134             }
135            
136 0           return $net;
137             }
138              
139             # returns the protocol from the current line. $line is a ref
140             sub _getproto {
141 0     0     my ($self, $line) = @_;
142 0 0         croak("\$line must be a reference") unless ref $line;
143 0           my $proto;
144              
145 0           my $word = $self->_nextword($line);
146 0 0         if ($word eq 'object-group') {
147 0           $proto = $self->{walker}->obj( $self->_nextword($line) );
148             } else {
149 0           my $name = 'unnamed_proto_'.(++$self->{unnamed_proto});
150 0           my $conf = [ "object-group protocol $name", "protocol-object $word" ];
151 0           $proto = new PIX::Object('protocol', $name, $conf, $self->{walker});
152             }
153              
154 0           return $proto;
155             }
156              
157             # returns the next
158             sub _geticmp {
159 0     0     my ($self, $line) = @_;
160             }
161              
162             # returns the next service port(s) from the current line. $line is a ref
163             sub _getports {
164 0     0     my ($self, $line, $proto) = @_;
165 0 0         croak("\$line must be a reference") unless ref $line;
166 0           my $port;
167              
168 0   0       my $word = $self->_nextword($line) || return undef;
169 0 0 0       if ($word eq 'object-group') {
    0 0        
    0 0        
170 0           my $word2 = $self->_nextword($line);
171 0           my $obj = $self->{walker}->obj($word2);
172 0 0 0       $port = (defined($obj) and $obj->type =~ /service|icmp-type/) ? $obj : undef;
173             #print "OBJ=$obj == " . join(',',$obj->enumerate) . "\n" if $obj and $obj->type eq 'icmp-type';
174             # put the previous words back in the line, since it's going to
175             # be a valid network object-group
176 0 0         if (!$port) {
177 0           $self->_rewindword($word2, $line);
178 0           $self->_rewindword($word, $line);
179             }
180             } elsif ($word eq 'eq' || $word eq 'gt' || $word eq 'lt' || $word eq 'neg') {
181 0           my $op = $word;
182 0           $word = $self->_nextword($line);
183 0           my $name = 'unnamed_service_'.(++$self->{unnamed_service});
184 0           my $conf = [ "object-group service $name", "port-object $op $word" ];
185 0           $port = new PIX::Object('service', $name, $conf, $self->{walker});
186             } elsif ($word eq 'range') {
187 0           $word = $self->_nextword($line);
188 0           my $word2 = $self->_nextword($line);
189 0           my $name = 'unnamed_service_'.(++$self->{unnamed_service});
190 0           my $conf = [ "object-group service $name", "port-object range $word $word2" ];
191 0           $port = new PIX::Object('service', $name, $conf, $self->{walker});
192             } else { # any other values (eg: 'log') are ignored
193 0           $self->_rewindword($word, $line);
194 0           return undef;
195             }
196              
197             # save the newly created service group to the main walker object
198 0 0 0       if (defined $port and !defined $self->{walker}->obj($port->name)) {
199 0           $self->{walker}{objects}{$port->name} = $port;
200             }
201              
202 0           return $port;
203             }
204              
205             =item B
206              
207             =over
208              
209             Returns the total elements (ACE) in the access-list.
210             B It's not wise to call this over and over again. Store the result
211             in a variable and use that variable if you need to use this result in multiple
212             places.
213              
214             =back
215              
216             =cut
217             sub elements {
218 0     0 1   my $self = shift;
219 0           my $total = 0;
220 0           $total += $_->elements for $self->lines;
221 0           return $total;
222             }
223              
224             =item B
225              
226             =over
227              
228             Returns all lines of the ACL. Each line is an B object.
229              
230             =back
231              
232             =cut
233 0     0 1   sub lines { @{$_[0]->{acl}} }
  0            
234              
235             =item B
236              
237             =over
238              
239             Returns the name of the ACL
240              
241             =back
242              
243             =cut
244 0     0 1   sub name { $_[0]->{name} }
245              
246             =item B
247              
248             =over
249              
250             Pretty prints the ACL. Tries to make it easy to read. If object-group's are used
251             the names are printed instead of IP's if more than a single IP is present for a line.
252              
253             $any is an optional string that will be used for any IP that represents 'ANY',
254             defaults to: 0.0.0.0/0. It's useful to change this to 'ANY' to make the output
255             easier to read.
256              
257             1) permit (tcp) 192.168.0.0/24 -> 0.0.0.0/0 [Web_Services_tcp: 80,443]
258             10) deny (ip) 0.0.0.0/0 -> 0.0.0.0/0
259              
260             =back
261              
262             =cut
263             sub print {
264 0     0 1   my $self = shift;
265 0           my $any = shift; # PIX::Accesslist::Line will default to 0.0.0.0/0
266 0           my $output = "----- Access-list $self->{name} -----\n";
267 0           $output .= $_->print($any) . "\n" for $self->lines;
268 0           return $output;
269             }
270              
271             # $line is a ref to a scalar string. The word returned is removed from the string
272 0 0   0     sub _nextword { (${$_[1]} =~ s/^\s*(\S+)\s*//) ? $1 : undef; }
  0            
273 0     0     sub _nextline { $_[0]->{linenum}++; shift @{$_[0]->{config_block}} }
  0            
  0            
274 0     0     sub _reset { $_[0]->{linenum} = 0; $_[0]->{config_block} = $_[0]->{config} }
  0            
275 0     0     sub _rewind { unshift @{$_[0]->{config_block}}, $_[1] }
  0            
276 0     0     sub _rewindword { ${$_[2]} = $_[1] . " " . ${$_[2]} }
  0            
  0            
277              
278             1;
279              
280             =pod
281              
282             =head1 AUTHOR
283              
284             Jason Morriss
285              
286             =head1 BUGS
287              
288             Please report any bugs or feature requests to
289             C, or through the web interface at
290             L.
291             I will be notified, and then you'll automatically be notified of progress on
292             your bug as I make changes.
293              
294             =head1 SUPPORT
295              
296             perldoc PIX::Walker
297              
298             perldoc PIX::Accesslist
299             perldoc PIX::Accesslist::Line
300              
301             perldoc PIX::Object
302             perldoc PIX::Object::network
303             perldoc PIX::Object::service
304             perldoc PIX::Object::protocol
305             perldoc PIX::Object::icmp_type
306              
307             =head1 COPYRIGHT & LICENSE
308              
309             Copyright 2006-2008 Jason Morriss, all rights reserved.
310              
311             This program is free software; you can redistribute it and/or modify it
312             under the same terms as Perl itself.
313              
314             =cut