shorewall_code/Shorewall-perl/Shorewall/Hosts.pm
2007-07-01 00:02:38 +00:00

172 lines
4.7 KiB
Perl

#
# Shorewall-perl 4.0 -- /usr/share/shorewall-perl/Shorewall/Hosts.pm
#
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
# (c) 2007 - Tom Eastep (teastep@shorewall.net)
#
# Complete documentation is available at http://shorewall.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of Version 2 of the GNU General Public License
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# This module contains the code for dealing with the /etc/shorewall/hosts
# file.
#
package Shorewall::Hosts;
require Exporter;
use Shorewall::Common;
use Shorewall::Config;
use Shorewall::IPAddrs;
use Shorewall::Zones;
use Shorewall::Interfaces;
use strict;
our @ISA = qw(Exporter);
our @EXPORT = qw( validate_hosts_file find_hosts_by_option );
our @EXPORT_OK = ();
our $VERSION = '4.0.0';
#
# Validates the hosts file. Generates entries in %zone{..}{hosts}
#
sub validate_hosts_file()
{
my %validoptions = (
blacklist => 1,
maclist => 1,
norfc1918 => 1,
nosmurfs => 1,
routeback => 1,
routefilter => 1,
tcpflags => 1,
);
my $ipsec = 0;
my $first_entry = 1;
my $fn = open_file 'hosts';
while ( read_a_line ) {
if ( $first_entry ) {
progress_message2 "$doing $fn...";
$first_entry = 0;
}
my ($zone, $hosts, $options ) = split_line 2, 3, 'hosts file';
my $zoneref = $zones{$zone};
my $type = $zoneref->{type};
fatal_error "Unknown ZONE ($zone)" unless $type;
fatal_error 'Firewall zone not allowed in ZONE column of hosts record' if $type eq 'firewall';
my $interface;
if ( $hosts =~ /^([\w.@%-]+\+?):(.*)$/ ) {
$interface = $1;
$hosts = $2;
$zoneref->{options}{complex} = 1 if $hosts =~ /^\+/;
fatal_error "Unknown interface ($interface)" unless $interfaces{$interface}{root};
} else {
fatal_error "Invalid HOST(S) column contents: $hosts";
}
if ( $type eq 'bport4' ) {
if ( $zoneref->{bridge} eq '' ) {
fatal_error 'Bridge Port Zones may only be associated with bridge ports' unless $interfaces{$interface}{options}{port};
$zoneref->{bridge} = $interfaces{$interface}{bridge};
} elsif ( $zoneref->{bridge} ne $interfaces{$interface}{bridge} ) {
fatal_error "Interface $interface is not a port on bridge $zoneref->{bridge}";
}
}
my $optionsref = {};
if ( $options ne '-' ) {
my @options = split ',', $options;
my %options;
for my $option ( @options )
{
if ( $option eq 'ipsec' ) {
$type = 'ipsec';
$zoneref->{options}{complex} = 1;
$ipsec = 1;
} elsif ( $validoptions{$option}) {
$options{$option} = 1;
} else {
fatal_error "Invalid option ($option)";
}
}
$optionsref = \%options;
}
#
# Looking for the '!' at the beginning of a list element is more straight-foward than looking for it in the middle.
#
# Be sure we don't have a ',!' in the original
#
fatal_error "Invalid hosts list" if $hosts =~ /,!/;
#
# Now add a comma before '!'. Do it globally - add_group_to_zone() correctly checks for multiple exclusions
#
$hosts =~ s/!/,!/g;
#
# Take care of case where the hosts list begins with '!'
#
$hosts = join( '', ALLIPv4 , $hosts ) if substr($hosts, 0, 2 ) eq ',!';
add_group_to_zone( $zone, $type , $interface, [ split( ',', $hosts ) ] , $optionsref);
progress_message " Host \"$line\" validated";
}
$capabilities{POLICY_MATCH} = '' unless $ipsec || haveipseczones;
}
#
# Returns a reference to a array of host entries. Each entry is a
# reference to an array containing ( interface , polciy match type {ipsec|none} , network );
#
sub find_hosts_by_option( $ ) {
my $option = $_[0];
my @hosts;
for my $zone ( grep $zones{$_}{type} ne 'firewall' , @zones ) {
while ( my ($type, $interfaceref) = each %{$zones{$zone}{hosts}} ) {
while ( my ( $interface, $arrayref) = ( each %{$interfaceref} ) ) {
for my $host ( @{$arrayref} ) {
if ( $host->{options}{$option} ) {
for my $net ( @{$host->{hosts}} ) {
push @hosts, [ $interface, $host->{ipsec} , $net ];
}
}
}
}
}
}
for my $interface ( @interfaces ) {
if ( ! $interfaces{$interface}{zone} && $interfaces{$interface}{options}{$option} ) {
push @hosts, [ $interface, 'none', ALLIPv4 ];
}
}
\@hosts;
}
1;