line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package PIX::Walker; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
22893
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
34
|
|
4
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
27
|
|
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
78
|
|
7
|
1
|
|
|
1
|
|
533
|
use PIX::Object; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
28
|
|
8
|
1
|
|
|
1
|
|
569
|
use PIX::Accesslist; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
37
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
BEGIN { |
11
|
1
|
|
|
1
|
|
7
|
use Exporter; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
116
|
|
12
|
|
|
|
|
|
|
|
13
|
1
|
|
|
1
|
|
2
|
our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); |
14
|
|
|
|
|
|
|
|
15
|
1
|
|
|
|
|
3
|
$VERSION = '1.10'; |
16
|
|
|
|
|
|
|
|
17
|
1
|
|
|
|
|
17
|
@ISA = qw(Exporter); |
18
|
1
|
|
|
|
|
2
|
@EXPORT = qw(); |
19
|
1
|
|
|
|
|
3
|
@EXPORT_OK = qw(); |
20
|
1
|
|
|
|
|
2661
|
%EXPORT_TAGS = (); |
21
|
|
|
|
|
|
|
} |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=pod |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 NAME |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
PIX::Walker - Process Cisco PIX configs and 'walk' access-lists |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 SYNOPSIS |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
PIX::Walker is an object that allows you to process PIX (and ASA) firewall |
32
|
|
|
|
|
|
|
configs and 'walk' an access-list for matches. PIX OS versions 6 and 7 are |
33
|
|
|
|
|
|
|
supported. Note, ACL's that use the 'interface' keyword will not match properly |
34
|
|
|
|
|
|
|
since there is no way for the Walker to match an IP to an interface, at least |
35
|
|
|
|
|
|
|
not yet. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
'Loose' ACL matching performed. This means that you can specify as little as a |
38
|
|
|
|
|
|
|
single IP to match what line(s) that IP would match in the ACL on the firewall. |
39
|
|
|
|
|
|
|
Or you can provide every detail including source/dest IPs, ports, and protocol |
40
|
|
|
|
|
|
|
to match a specific line of an ACL. Loose matching allows you to see potential |
41
|
|
|
|
|
|
|
lines in a large ruleset that a single source or destination IP might match. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
More than just the first line match can be returned. If your search criteria can |
44
|
|
|
|
|
|
|
technically match multiple lines they will all be returned. This is useful for |
45
|
|
|
|
|
|
|
seeing extra matches in your ACL that might also match and can help you optimize |
46
|
|
|
|
|
|
|
your ACL. |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=head1 EXAMPLE |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
use PIX::Walker; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
my $config = "... firewall config buffer or filename ..."; |
53
|
|
|
|
|
|
|
my $fw = new PIX::Walker($config); |
54
|
|
|
|
|
|
|
my $acl = $fw->acl("outside_access") || die("ACL does not exist"); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
my $matched = 0; |
57
|
|
|
|
|
|
|
# search each line of the ACL for possible matches |
58
|
|
|
|
|
|
|
foreach my $line ($acl->lines) { |
59
|
|
|
|
|
|
|
if ($line->match( |
60
|
|
|
|
|
|
|
source => "10.0.1.100", |
61
|
|
|
|
|
|
|
dest => "192.168.1.3", |
62
|
|
|
|
|
|
|
dport => "80", # dest port |
63
|
|
|
|
|
|
|
proto => "tcp")) { |
64
|
|
|
|
|
|
|
if (!$matched++) { |
65
|
|
|
|
|
|
|
print "Matched ACL " . $acl->name . |
66
|
|
|
|
|
|
|
" (" . $acl->elements . " ACE)\n"; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
print $line->print, "\n"; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=head1 METHODS |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=over |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=cut |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=item B |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=over |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
Returns a new PIX::Walker object using the $config string passed in. The |
83
|
|
|
|
|
|
|
configuration is processed and broken out into various objects automatically. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
The $config string is either a full string buffer containing the configuration |
86
|
|
|
|
|
|
|
of a firewall or is used as a filename to read the configuration from, using |
87
|
|
|
|
|
|
|
various filename formats (tried with and without any extension on the filename) |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
* {$config} |
90
|
|
|
|
|
|
|
* {$config}.conf |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
If $not_a_file is true then the $config string is never checked against the |
93
|
|
|
|
|
|
|
file system. |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=back |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=cut |
98
|
|
|
|
|
|
|
sub new { |
99
|
0
|
|
|
0
|
1
|
|
my $proto = shift; |
100
|
0
|
|
0
|
|
|
|
my $class = ref($proto) || $proto; |
101
|
0
|
|
|
|
|
|
my $self = { debug => 0 }; |
102
|
0
|
|
|
|
|
|
my ($fw_config, $not_a_file) = @_; |
103
|
0
|
|
|
|
|
|
my $conf; |
104
|
0
|
0
|
|
|
|
|
croak("Must provide firewall configuration") unless $fw_config; |
105
|
|
|
|
|
|
|
|
106
|
0
|
|
|
|
|
|
bless($self, $class); |
107
|
|
|
|
|
|
|
|
108
|
0
|
|
|
|
|
|
$self->{firewall} = ''; |
109
|
|
|
|
|
|
|
|
110
|
0
|
|
|
|
|
|
my ($file, $host); |
111
|
|
|
|
|
|
|
|
112
|
0
|
|
|
|
|
|
$file = (split(/\n/, $fw_config, 2))[0]; |
113
|
0
|
|
|
|
|
|
$host = (split(/\./, $file, 2))[0]; |
114
|
|
|
|
|
|
|
|
115
|
0
|
0
|
0
|
|
|
|
if (!$not_a_file and -f $file) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
116
|
0
|
0
|
|
|
|
|
open(F, "<$file") or croak("Error opening file for reading: $!"); |
117
|
0
|
|
|
|
|
|
$conf = join('', ); |
118
|
0
|
|
|
|
|
|
close(F); |
119
|
|
|
|
|
|
|
} elsif (!$not_a_file and -f "$file.conf") { |
120
|
0
|
0
|
|
|
|
|
open(F, "<$file.conf") or croak("Error opening file for reading: $!"); |
121
|
0
|
|
|
|
|
|
$conf = join('', ); |
122
|
0
|
|
|
|
|
|
close(F); |
123
|
|
|
|
|
|
|
} elsif (!$not_a_file and -f $host) { |
124
|
0
|
0
|
|
|
|
|
open(F, "<$host") or croak("Error opening file for reading: $!"); |
125
|
0
|
|
|
|
|
|
$conf = join('', ); |
126
|
0
|
|
|
|
|
|
close(F); |
127
|
|
|
|
|
|
|
} elsif (!$not_a_file and -f "$host.conf") { |
128
|
0
|
0
|
|
|
|
|
open(F, "<$host.conf") or croak("Error opening file for reading: $!"); |
129
|
0
|
|
|
|
|
|
$conf = join('', ); |
130
|
0
|
|
|
|
|
|
close(F); |
131
|
|
|
|
|
|
|
} else { |
132
|
0
|
|
|
|
|
|
$conf = $fw_config; |
133
|
|
|
|
|
|
|
} |
134
|
0
|
0
|
|
|
|
|
croak("No firewall configuration found") unless $conf; |
135
|
0
|
|
|
|
|
|
$self->{config} = [ split(/\n/, $conf) ]; |
136
|
0
|
|
|
|
|
|
$self->{config_block} = [ split(/\n/, $conf) ]; |
137
|
|
|
|
|
|
|
|
138
|
0
|
|
|
|
|
|
$self->_init; |
139
|
0
|
|
|
|
|
|
$self->_process; |
140
|
|
|
|
|
|
|
|
141
|
0
|
|
|
|
|
|
return $self; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub _init { |
145
|
0
|
|
|
0
|
|
|
my $self = shift; |
146
|
0
|
|
|
|
|
|
$self->{objects} = {}; |
147
|
0
|
|
|
|
|
|
$self->{acls} = {}; |
148
|
0
|
|
|
|
|
|
$self->{alias} = {}; |
149
|
0
|
|
|
|
|
|
$self->{ports} = { |
150
|
|
|
|
|
|
|
# insert static entries here... |
151
|
|
|
|
|
|
|
'imap4' => '143', |
152
|
|
|
|
|
|
|
'h323' => '1720', |
153
|
|
|
|
|
|
|
'sqlnet' => '1521', |
154
|
|
|
|
|
|
|
'pcanywhere-data' => '5631', |
155
|
|
|
|
|
|
|
'pcanywhere-status' => '5632', |
156
|
|
|
|
|
|
|
'citrix-ica' => '1494', |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# cisco PIX defined |
159
|
|
|
|
|
|
|
# (there may be more now; I have not updated this in awhile) |
160
|
|
|
|
|
|
|
'aol' => '5190', |
161
|
|
|
|
|
|
|
'bgp' => '179', |
162
|
|
|
|
|
|
|
'biff' => '512', |
163
|
|
|
|
|
|
|
'bootpc' => '68', |
164
|
|
|
|
|
|
|
'bootps' => '67', |
165
|
|
|
|
|
|
|
'chargen' => '19', |
166
|
|
|
|
|
|
|
'cmd' => '514', |
167
|
|
|
|
|
|
|
'rsh' => '514', |
168
|
|
|
|
|
|
|
'daytime' => '13', |
169
|
|
|
|
|
|
|
'discard' => '9', |
170
|
|
|
|
|
|
|
'domain' => '53', |
171
|
|
|
|
|
|
|
'dnsix' => '195', |
172
|
|
|
|
|
|
|
'echo' => '7', |
173
|
|
|
|
|
|
|
'exec' => '512', |
174
|
|
|
|
|
|
|
'finger' => '79', |
175
|
|
|
|
|
|
|
'ftp' => '21', |
176
|
|
|
|
|
|
|
'ftp-data' => '20', |
177
|
|
|
|
|
|
|
'gopher' => '70', |
178
|
|
|
|
|
|
|
'hostname' => '101', |
179
|
|
|
|
|
|
|
'https' => '443', |
180
|
|
|
|
|
|
|
'nameserver' => '42', |
181
|
|
|
|
|
|
|
'ident' => '113', |
182
|
|
|
|
|
|
|
'irc' => '194', |
183
|
|
|
|
|
|
|
'isakmp' => '500', |
184
|
|
|
|
|
|
|
'klogin' => '543', |
185
|
|
|
|
|
|
|
'kshell' => '544', |
186
|
|
|
|
|
|
|
'ldap' => '389', |
187
|
|
|
|
|
|
|
'ldaps' => '636', |
188
|
|
|
|
|
|
|
'lotusnotes' => '1352', |
189
|
|
|
|
|
|
|
'lpd' => '515', |
190
|
|
|
|
|
|
|
'login' => '513', |
191
|
|
|
|
|
|
|
'mobile-ip' => '434', |
192
|
|
|
|
|
|
|
'netbios-ns' => '137', |
193
|
|
|
|
|
|
|
'netbios-dgm' => '138', |
194
|
|
|
|
|
|
|
'netbios-ssn' => '139', |
195
|
|
|
|
|
|
|
'nntp' => '119', |
196
|
|
|
|
|
|
|
'ntp' => '123', |
197
|
|
|
|
|
|
|
'pim-auto-rp' => '496', |
198
|
|
|
|
|
|
|
'pop2' => '109', |
199
|
|
|
|
|
|
|
'pop3' => '110', |
200
|
|
|
|
|
|
|
'pptp' => '1723', |
201
|
|
|
|
|
|
|
'radius-acct' => '1813', |
202
|
|
|
|
|
|
|
'rip' => '520', |
203
|
|
|
|
|
|
|
'rtsp' => '554', |
204
|
|
|
|
|
|
|
'sip' => '5060', |
205
|
|
|
|
|
|
|
'smtp' => '25', |
206
|
|
|
|
|
|
|
'snmp' => '161', |
207
|
|
|
|
|
|
|
'snmptrap' => '162', |
208
|
|
|
|
|
|
|
'ssh' => '22', |
209
|
|
|
|
|
|
|
'sunrpc' => '111', |
210
|
|
|
|
|
|
|
'syslog' => '514', |
211
|
|
|
|
|
|
|
'tacacs' => '49', |
212
|
|
|
|
|
|
|
'talk' => '517', |
213
|
|
|
|
|
|
|
'telnet' => '23', |
214
|
|
|
|
|
|
|
'tftp' => '69', |
215
|
|
|
|
|
|
|
'time' => '37', |
216
|
|
|
|
|
|
|
'uucp' => '540', |
217
|
|
|
|
|
|
|
'who' => '513', |
218
|
|
|
|
|
|
|
'whois' => '43', |
219
|
|
|
|
|
|
|
'www' => '80', |
220
|
|
|
|
|
|
|
'xdmcp' => '177', |
221
|
|
|
|
|
|
|
}; |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# Look for services files (nmap is better) and build a translation |
224
|
|
|
|
|
|
|
# table. This reads ALL the files listed and merges the results into a |
225
|
|
|
|
|
|
|
# single hash lookup table. the first name-to-port lookup found is used |
226
|
|
|
|
|
|
|
# and is not overwritten. This obviously only works on Linux. |
227
|
0
|
|
|
|
|
|
my @files = qw( /usr/local/share/nmap/nmap-services /usr/share/nmap/nmap-services /etc/services ); |
228
|
0
|
|
|
|
|
|
while (defined(my $file = shift @files)) { |
229
|
0
|
0
|
|
|
|
|
next unless -f $file; |
230
|
0
|
0
|
|
|
|
|
open(F, "<$file") or next; |
231
|
0
|
|
|
|
|
|
while (defined(my $line = )) { |
232
|
0
|
|
|
|
|
|
$line =~ s/^\s+//; |
233
|
0
|
|
|
|
|
|
$line =~ s/\s+$//; |
234
|
0
|
0
|
0
|
|
|
|
next if $line eq '' or $line =~ /^#/; |
235
|
0
|
|
|
|
|
|
my ($name, $str) = split(/\s+/, $line); |
236
|
0
|
|
|
|
|
|
my $port = (split(/\//, $str))[0]; |
237
|
0
|
0
|
|
|
|
|
$self->{ports}{$name} = $port unless exists $self->{ports}{$name}; |
238
|
|
|
|
|
|
|
} |
239
|
0
|
|
|
|
|
|
close(F); |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# INTERNAL: processes the configuration and breaks things apart into different bits |
244
|
|
|
|
|
|
|
sub _process { |
245
|
0
|
|
|
0
|
|
|
my $self = shift; |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# continue until all config lines are gone |
248
|
0
|
|
|
|
|
|
while (defined(my $line = $self->_nextline)) { |
249
|
0
|
0
|
|
|
|
|
if ($line =~ /^object-group (\S+) (\S+)/i) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
250
|
0
|
|
|
|
|
|
my ($type, $name) = ($1,$2); |
251
|
0
|
|
|
|
|
|
my $conf = [ $line ]; |
252
|
0
|
|
|
|
|
|
$line = $self->_nextline; |
253
|
0
|
|
0
|
|
|
|
while (defined $line && $line =~ /^\s*(\w+-object|desc)/) { |
254
|
0
|
|
|
|
|
|
push(@$conf, $line); |
255
|
0
|
|
|
|
|
|
$line = $self->_nextline; |
256
|
|
|
|
|
|
|
} |
257
|
0
|
|
|
|
|
|
$self->_rewind($line); # rewind 1 line so we don't skip past it on the next iteration |
258
|
0
|
|
|
|
|
|
$self->add_obj($type, $name, $conf); |
259
|
|
|
|
|
|
|
} elsif ($line =~ /^access-list (\S+)/) { |
260
|
0
|
|
|
|
|
|
my $name = $1; |
261
|
0
|
0
|
|
|
|
|
next if $name eq 'compiled'; |
262
|
0
|
|
|
|
|
|
my $conf = [ $line ]; |
263
|
0
|
|
|
|
|
|
$line = $self->_nextline; |
264
|
0
|
|
0
|
|
|
|
while (defined $line && $line =~ /^access-list $name/) { |
265
|
0
|
|
|
|
|
|
push(@$conf, $line); |
266
|
0
|
|
|
|
|
|
$line = $self->_nextline; |
267
|
|
|
|
|
|
|
} |
268
|
0
|
|
|
|
|
|
$self->_rewind($line); |
269
|
0
|
|
|
|
|
|
$self->add_acl($name, $conf); |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
} elsif ($line =~ /^name (\S+) (\S+)/) { # ignore descriptions |
272
|
0
|
|
|
|
|
|
$self->{alias}{$2} = $1; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=item B |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
=over |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
Returns an PIX::Accesslist object for the ACL named by $name. |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=back |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=cut |
287
|
|
|
|
|
|
|
sub acl { |
288
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
289
|
0
|
|
|
|
|
|
my $name = shift; |
290
|
0
|
0
|
|
|
|
|
return exists $self->{acls}{$name} ? $self->{acls}{$name} : undef; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=item B |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=over |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
Returns an array of access-list strings for each access-list |
298
|
|
|
|
|
|
|
found in the firewall configuration. Returns undef if there is no |
299
|
|
|
|
|
|
|
matching ACL. Use walker->acl('acl_name') to retrieve the actual |
300
|
|
|
|
|
|
|
PIX::Accesslist object. |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
=back |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=cut |
305
|
0
|
|
|
0
|
1
|
|
sub acls { keys %{$_[0]->{acls}} } |
|
0
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=item B |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
=over |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
Add's an access-list object to the PIX::Walker object. $conf is an arrayref |
312
|
|
|
|
|
|
|
of the configuration lines that make up the access-list and can be empty. |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=back |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=cut |
317
|
|
|
|
|
|
|
sub add_acl { |
318
|
0
|
|
|
0
|
1
|
|
my ($self, $name, $conf) = @_; |
319
|
0
|
|
0
|
|
|
|
return $self->{acls}{$name} = new PIX::Accesslist($name, $conf || [], $self); |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=item B |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=over |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
Add's an object-group object to the PIX::Walker object. $conf is an arrayref |
327
|
|
|
|
|
|
|
of the configuration lines that make up the object-group and can be empty. |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=back |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=cut |
332
|
|
|
|
|
|
|
sub add_obj { |
333
|
0
|
|
|
0
|
1
|
|
my ($self, $type, $name, $conf) = @_; |
334
|
0
|
|
0
|
|
|
|
return $self->{objects}{$name} = new PIX::Object($type, $name, $conf || [], $self); |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=item B |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=over |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
Returns the IP of the alias given in $alias. If no alias is found than the |
342
|
|
|
|
|
|
|
string is returned unchanged. |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=back |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=cut |
347
|
|
|
|
|
|
|
sub alias { |
348
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
349
|
0
|
|
|
|
|
|
my $alias = shift; |
350
|
0
|
0
|
|
|
|
|
return exists $self->{alias}{$alias} ? $self->{alias}{$alias} : $alias; |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=item B |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=over |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Matches the IP to an existing network-group. Does not validate it within any ACL. |
358
|
|
|
|
|
|
|
If a single group is matched a scalar is returned with the name, otherwise an |
359
|
|
|
|
|
|
|
array reference is returned containing all matches. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
* I<$ip> is an IP address to look for. |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
* I<$trace> is an optional reference to a trace buffer. |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
If an IP is found in a nested group the trace will allow you to find out where |
366
|
|
|
|
|
|
|
it was nested. See L for more information. |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
=back |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=cut |
371
|
|
|
|
|
|
|
sub findip { |
372
|
0
|
|
|
0
|
1
|
|
my ($self, $ip, $trace) = @_; |
373
|
0
|
|
|
|
|
|
my $found = []; |
374
|
|
|
|
|
|
|
|
375
|
0
|
|
|
|
|
|
foreach my $obj (keys %{$self->{objects}}) { |
|
0
|
|
|
|
|
|
|
376
|
0
|
|
|
|
|
|
my $grp = $self->{objects}{$obj}; |
377
|
0
|
0
|
|
|
|
|
next unless $grp->type eq 'network'; # we only care about network groups |
378
|
0
|
|
|
|
|
|
my $localtrace = []; |
379
|
0
|
|
|
|
|
|
my $match = $grp->matchip($ip, $localtrace); |
380
|
0
|
0
|
|
|
|
|
if ($match) { |
381
|
0
|
0
|
|
|
|
|
push(@$trace, $localtrace) if defined $trace; |
382
|
0
|
|
|
|
|
|
push(@$found, $match); |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
} |
385
|
0
|
0
|
|
|
|
|
if (scalar @$found) { |
386
|
0
|
|
|
|
|
|
my %u; |
387
|
0
|
|
|
|
|
|
my @uniq = grep { !$u{$_}++ } sort @$found; |
|
0
|
|
|
|
|
|
|
388
|
0
|
0
|
|
|
|
|
return (scalar @uniq == 1) ? $uniq[0] : \@uniq; |
389
|
|
|
|
|
|
|
} |
390
|
0
|
|
|
|
|
|
return undef; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=item B |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=over |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
Matches the PORT to an existing service-group. Does not validate it within any ACL. |
398
|
|
|
|
|
|
|
If a single group is matched a scalar is returned with the name, otherwise an |
399
|
|
|
|
|
|
|
array reference is returned containing all matches. |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
* I<$port> is the PORT to look for. |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
* I<$trace> is an optional reference to a trace buffer. |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
If a PORT is found in a nested group the trace will allow you to find out where |
406
|
|
|
|
|
|
|
it was nested. See L for more information. |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=back |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=cut |
411
|
|
|
|
|
|
|
sub findport { |
412
|
0
|
|
|
0
|
1
|
|
my ($self, $port, $trace) = @_; |
413
|
0
|
|
|
|
|
|
my $found = []; |
414
|
|
|
|
|
|
|
|
415
|
0
|
|
|
|
|
|
foreach my $obj (keys %{$self->{objects}}) { |
|
0
|
|
|
|
|
|
|
416
|
0
|
|
|
|
|
|
my $grp = $self->{objects}{$obj}; |
417
|
0
|
0
|
|
|
|
|
next unless $grp->type eq 'service'; # we only care about service groups |
418
|
0
|
|
|
|
|
|
my $localtrace = []; |
419
|
0
|
|
|
|
|
|
my $match = $grp->matchport($port, $localtrace); |
420
|
0
|
0
|
|
|
|
|
if ($match) { |
421
|
0
|
0
|
|
|
|
|
push(@$trace, $localtrace) if defined $trace; |
422
|
0
|
|
|
|
|
|
push(@$found, $match); |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
} |
425
|
0
|
0
|
|
|
|
|
if (scalar @$found) { |
426
|
0
|
|
|
|
|
|
my %u; |
427
|
0
|
|
|
|
|
|
my @uniq = grep { !$u{$_}++ } sort @$found; |
|
0
|
|
|
|
|
|
|
428
|
0
|
0
|
|
|
|
|
return (scalar @uniq == 1) ? $uniq[0] : \@uniq; |
429
|
|
|
|
|
|
|
} |
430
|
0
|
|
|
|
|
|
return undef; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=item B |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=over |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
Returns an B object for the object-group that matches the $name given. |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=back |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=cut |
442
|
|
|
|
|
|
|
sub obj { |
443
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
444
|
0
|
|
|
|
|
|
my $name = shift; |
445
|
0
|
0
|
|
|
|
|
return exists $self->{objects}{$name} ? $self->{objects}{$name} : undef; |
446
|
|
|
|
|
|
|
} |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
=item B |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=over |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Returns an array of object-group strings for each object-group found in the |
453
|
|
|
|
|
|
|
firewall configuration. If $type is specified only groups matching that type |
454
|
|
|
|
|
|
|
are returned. |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
Returns undef if there are no object-groups. Use walker->obj('obj_name') to |
457
|
|
|
|
|
|
|
retreive the actual PIX::Object object. |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=back |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=cut |
462
|
|
|
|
|
|
|
sub objs { |
463
|
0
|
|
|
0
|
1
|
|
my ($self, $type) = @_; |
464
|
0
|
|
|
|
|
|
$type = lc $type; |
465
|
0
|
0
|
|
|
|
|
if ($type) { |
466
|
0
|
|
|
|
|
|
return grep { $self->{objects}{$_}->type eq $type } keys %{$self->{objects}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
} else { |
468
|
0
|
|
|
|
|
|
return keys %{$self->{objects}}; |
|
0
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=item B |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=over |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
Returns the port NUMBER of the port name given. This function will DIE() if the |
477
|
|
|
|
|
|
|
port name is not known. This is harsh because the routines that use this |
478
|
|
|
|
|
|
|
function will not work if a single port lookup fails (not being able to lookup |
479
|
|
|
|
|
|
|
a port number can cause some of your acl searching to fail). This function is |
480
|
|
|
|
|
|
|
meant to be used internally only. |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=back |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=cut |
485
|
|
|
|
|
|
|
sub portnum { |
486
|
0
|
|
|
0
|
1
|
|
my ($self, $port) = @_; |
487
|
0
|
0
|
|
|
|
|
return $port if $port =~ /^\d+$/; |
488
|
|
|
|
|
|
|
# using die() below is a bit harsh but I don't have a better way to deal with it for now. |
489
|
0
|
0
|
|
|
|
|
return exists $self->{ports}{$port} ? $self->{ports}{$port} : die("Unknown port name '$port'"); |
490
|
|
|
|
|
|
|
} |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=item B |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=over |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
Prints out the trace dump given. This will allow you to see where IP's and PORT's are being |
497
|
|
|
|
|
|
|
matched within their object-groups even if they are nested. |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
=over |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
$matched = $fw->findip($ip, $trace); |
502
|
|
|
|
|
|
|
$fw->tracedump($trace); |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=back |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=cut |
507
|
|
|
|
|
|
|
sub tracedump { |
508
|
0
|
|
|
0
|
1
|
|
my ($self, $trace) = @_; |
509
|
0
|
0
|
|
|
|
|
return '' unless defined $trace; |
510
|
0
|
0
|
|
|
|
|
print "\nMatch Trace: \n" if @$trace; |
511
|
|
|
|
|
|
|
# use Data::Dumper; print Dumper($trace); return; |
512
|
0
|
|
|
|
|
|
foreach my $tr (@$trace) { |
513
|
0
|
|
|
|
|
|
my $idx = 0; |
514
|
0
|
|
|
|
|
|
for (my $i=0; $i<@$tr; $i=$i+3) { |
515
|
0
|
|
|
|
|
|
my ($name, $line, $extra) = @$tr[$i..$i+2]; |
516
|
|
|
|
|
|
|
# print "\t"x($idx++) . $name; |
517
|
0
|
0
|
|
|
|
|
print " -> " if $idx++; |
518
|
0
|
|
|
|
|
|
print $name; |
519
|
0
|
0
|
|
|
|
|
print " (match: $extra)" if $extra; |
520
|
0
|
0
|
|
|
|
|
print " (idx: $line)" if $line; |
521
|
|
|
|
|
|
|
# print "\n"; |
522
|
|
|
|
|
|
|
} |
523
|
0
|
|
|
|
|
|
print "\n"; |
524
|
|
|
|
|
|
|
} |
525
|
0
|
|
|
|
|
|
print "\n"; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
|
528
|
0
|
|
|
0
|
|
|
sub _nextline { shift @{$_[0]->{config_block}} } |
|
0
|
|
|
|
|
|
|
529
|
0
|
|
|
0
|
|
|
sub _rewind { unshift @{$_[0]->{config_block}}, $_[1] } |
|
0
|
|
|
|
|
|
|
530
|
0
|
|
|
0
|
|
|
sub _reset { $_[0]->{config_block} = $_[0]->{config} } |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
sub total_config_lines { |
533
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
534
|
0
|
0
|
|
|
|
|
return 0 unless defined $self->{config}; |
535
|
0
|
|
|
|
|
|
return scalar @{$self->{config}}; |
|
0
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
} |
537
|
0
|
|
|
0
|
0
|
|
sub total_network_objects { my $self=shift; return scalar grep { $self->{objects}{$_}->{class} =~ /network$/ } keys %{$self->{objects}} } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
538
|
0
|
|
|
0
|
0
|
|
sub total_service_objects { my $self=shift; return scalar grep { $self->{objects}{$_}->{class} =~ /service$/ } keys %{$self->{objects}} } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
539
|
0
|
|
|
0
|
0
|
|
sub total_protocol_objects { my $self=shift; return scalar grep { $self->{objects}{$_}->{class} =~ /protocol$/ } keys %{$self->{objects}} } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
540
|
0
|
|
|
0
|
0
|
|
sub total_icmp_type_objects { my $self=shift; return scalar grep { $self->{objects}{$_}->{class} =~ /icmp_type$/ } keys %{$self->{objects}} } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
541
|
0
|
|
|
0
|
0
|
|
sub total_object_groups { return scalar keys %{$_[0]->{objects}} } |
|
0
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
##sub total_acl_lines { return scalar @{$_[0]->{acl}} } |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
1; |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
=head1 AUTHOR |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
Jason Morriss |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=head1 BUGS |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
553
|
|
|
|
|
|
|
C, or through the web interface at |
554
|
|
|
|
|
|
|
L. |
555
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress on |
556
|
|
|
|
|
|
|
your bug as I make changes. |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
=head1 SUPPORT |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
perldoc PIX::Walker |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
perldoc PIX::Accesslist |
563
|
|
|
|
|
|
|
perldoc PIX::Accesslist::Line |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
perldoc PIX::Object |
566
|
|
|
|
|
|
|
perldoc PIX::Object::network |
567
|
|
|
|
|
|
|
perldoc PIX::Object::service |
568
|
|
|
|
|
|
|
perldoc PIX::Object::protocol |
569
|
|
|
|
|
|
|
perldoc PIX::Object::icmp_type |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
B - For pushing me to make this module and for supplying me with |
574
|
|
|
|
|
|
|
endless ideas. |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
Copyright 2006-2008 Jason Morriss, all rights reserved. |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
581
|
|
|
|
|
|
|
under the same terms as Perl itself. |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
=cut |