initial commit of file from CVS for e-smith-portforwarding on Wed 12 Jul 09:04:13 BST 2023

This commit is contained in:
Brian Read
2023-07-12 09:04:13 +01:00
parent e5466e0cdd
commit 4094d29da6
13 changed files with 1960 additions and 2 deletions

View File

@@ -0,0 +1,19 @@
{
my %FDB;
foreach my $proto ('TCP', 'UDP') {
$FDB{$proto} = esmith::ConfigDB->open("portforward_" . lc($proto))
|| esmith::ConfigDB->create("portforward_" . lc($proto));
my %rules = split ',', $DB->get_prop_and_delete('masq', "${proto}Forwards")
|| next;
foreach my $entry (keys %rules) {
my %props = ( type => 'forward' );
my ($addr, $port) = split ':', $rules{$entry};
$props{'DestHost'} = $addr;
$props{'DestPort'} = $port if $port;
$FDB{$proto}->new_record($entry, \%props);
}
}
}

View File

@@ -0,0 +1,168 @@
<!-- vim: ft=xml:
-->
<lexicon lang="en-us">
<entry>
<base>FORM_TITLE</base>
<trans>Configure Port Forwarding</trans>
</entry>
<entry>
<base>FIRST_PAGE_DESCRIPTION</base>
<trans><![CDATA[
<p>
You can use this panel to modify your firewall rules so
as to open a specific port on this server and forward it
to another port on another host. Doing so will permit
incoming traffic to directly access a private host on
your LAN.
</p>
<p>
WARNING: Misuse of this feature can seriously compromise the
security of your network. Do not use this feature
lightly, or without fully understanding the implications
of your actions.
</p>
]]>
</trans>
</entry>
<entry>
<base>CREATE_RULE</base>
<trans>Create portforwarding rule</trans>
</entry>
<entry>
<base>SUMMARY_ADD_DESC</base>
<trans>The following summarizes the port-forwarding rule
that you are about to add. If you are satisfied with the rule,
click the &quot;Add&quot; button. If you are not, click the
&quot;Cancel&quot; button.
</trans>
</entry>
<entry>
<base>SUMMARY_REMOVE_DESC</base>
<trans>The following summarizes the port-forwarding rule
that you are about to remove. If you are sure you want to
remove the rule, click the &quot;Remove&quot; button. If not,
click the &quot;Cancel&quot; button.
</trans>
</entry>
<entry>
<base>SHOW_FORWARDS</base>
<trans>
Below you will find a table summarizing the current
port-forwarding rules installed on this server. Click on the
&quot;Remove&quot; link to remove the corresponding rule.
</trans>
</entry>
<entry>
<base>NO_FORWARDS</base>
<trans>There are currently no forwarded ports on the system.</trans>
</entry>
<entry>
<base>CREATE_PAGE_DESCRIPTION</base>
<trans><![CDATA[
<p>Select the protocol, the port you wish to forward, the
destination host, and the port on the destination host
that you wish to forward to. If you wish to specify a port
range, enter the lower and upper boundaries separated by a
hyphen. The destination port may be left blank, which will
instruct the firewall to leave the source port
unaltered.</p>
]]>
</trans>
</entry>
<entry>
<base>LABEL_SOURCE_PORT</base>
<trans>Source Port(s)</trans>
</entry>
<entry>
<base>LABEL_PROTOCOL</base>
<trans>Protocol</trans>
</entry>
<entry>
<base>LABEL_DESTINATION_PORT</base>
<trans>Destination Port(s)</trans>
</entry>
<entry>
<base>LABEL_DESTINATION_HOST</base>
<trans>Destination Host IP Address</trans>
</entry>
<entry>
<base>LABEL_RULE_COMMENT</base>
<trans>Rule Comment</trans>
</entry>
<entry>
<base>LABEL_ALLOW_HOSTS</base>
<trans>Allow Hosts</trans>
</entry>
<entry>
<base>Port forwarding</base>
<trans>Port forwarding</trans>
</entry>
<entry>
<base>SUCCESS</base>
<trans>Your change to the port forwarding rules has been
successfully saved.
</trans>
</entry>
<entry>
<base>RULE_COMMENT</base>
<trans>Rule Comment</trans>
</entry>
<entry>
<base>ALLOW_HOSTS</base>
<trans>Allow Hosts</trans>
</entry>
<entry>
<base>ERR_NO_MASQ_RECORD</base>
<trans>Cannot retrieve masq record from the configuration
database.</trans>
</entry>
<entry>
<base>ERR_UNSUPPORTED_MODE</base>
<trans>Unsupported mode.</trans>
</entry>
<entry>
<base>ERR_CANNOT_REMOVE_NORULE</base>
<trans>Cannot remove non-existant rule.</trans>
</entry>
<entry>
<base>ERR_NONZERO_RETURN_EVENT</base>
<trans>Event returned a non-zero return value.</trans>
</entry>
<entry>
<base>ERR_BADPORT</base>
<trans>The ports must be a positive integer less than
65536.</trans>
</entry>
<entry>
<base>ERR_BADIP</base>
<trans>This does not appear to be an IP address. You must use
dotted-quad notation, and each of the four numbers should be less
than 256. ie: 192.168.0.5</trans>
</entry>
<entry>
<base>ERR_DUPRULE</base>
<trans>This rule has already been added, it cannot be added
twice.</trans>
</entry>
<entry>
<base>ERR_PORT_COLLISION</base>
<trans>
ERROR: This port or port range conflicts with an existing
rule. Please modify this new rule, or remove the old rule.
</trans>
</entry>
<entry>
<base>ERR_BADAHOST</base>
<trans>
This does not appear to be a valid IP address list.
ie: 192.168.0.1,192.168.1.1/24
</trans>
</entry>
<entry>
<base>IN_SERVERONLY</base>
<trans>
This server is currently in serveronly mode and portforwarding
is possible only to localhost.
</trans>
</entry>
</lexicon>

View File

@@ -0,0 +1,5 @@
/sbin/iptables -t nat --new-chain PortForwarding
/sbin/iptables -t nat --new-chain PortForwarding_1
/sbin/iptables -t nat --insert PREROUTING --jump PortForwarding
/sbin/iptables -t nat --append PortForwarding --destination $OUTERNET \
--jump PortForwarding_1

View File

@@ -0,0 +1,73 @@
{
my $pf_chain = "PortForwarding_\$\$";
$OUT .= "# Create a new PortForwarding chain\n";
$OUT .= "PFC=\$(/sbin/iptables --table nat ";
$OUT .= "--numeric --list PortForwarding |\\\n";
$OUT .= " sed -n '3s/ .*//p')\n";
$OUT .= " /sbin/iptables --table nat --new-chain $pf_chain\n";
my %FDB;
foreach my $protocol (qw(tcp udp))
{
my $uproto = uc $protocol;
$FDB{$protocol} = esmith::ConfigDB->open("portforward_$protocol")
|| die "Can't open portforward_$protocol database: $!\n";
foreach my $entry ( $FDB{$protocol}->get_all ) {
my $port = $entry->key;
my $ip = $entry->prop('DestHost');
my $dport = $entry->prop('DestPort') || $port;
$port =~ s/-/:/;
# Map canonical localhost back to our current external IP
$ip = '$OUTERNET' if ($ip eq 'localhost');
my $host_list = $entry->prop("AllowHosts") || '0.0.0.0/0';
foreach my $host (split(',', $host_list)) {
$OUT .= " /sbin/iptables --table nat --append $pf_chain";
# Set up local port to forward
$OUT .= " --proto $protocol --destination-port ${port}";
$OUT .= " --src $host" unless $host eq '0.0.0.0/0';
# Set up the remote port to forward to
$OUT .= " -j DNAT --to-destination $ip:$dport\n";
}
# And accept the incoming packets. Use the dport if there is one.
($port = $dport) =~ s/-/:/ if $dport;
# If this rule is forwarding to localhost, ExternalIP or LocalIP,
# then we must allow it on the INPUT chain instead of the FORWARD
# chain.
my $target_chain = (($ip eq '$OUTERNET') ?
"Inbound${uproto}_\$\$" : "Forwarded${uproto}_\$\$");
foreach my $access_type (("Allow", "Deny")) {
my $jump_target = (($access_type eq "Allow") ? "ACCEPT" : "denylog");
my $host_list = $entry->prop("${access_type}Hosts") || "";
$host_list = "0.0.0.0/0"
if (($host_list eq "") and ($access_type eq "Allow"));
foreach my $host (split(',', $host_list)) {
$OUT .= " /sbin/iptables -A $target_chain";
$OUT .= " --proto $protocol --dport $port \\\n ";
$OUT .= " --destination $ip" if ($ip ne '$OUTERNET');
$OUT .= " --src $host --jump $jump_target\n";
}
}
}
}
# having created a new PortForwarding chain, activate it and destroy
# the old.
$OUT .= " /sbin/iptables --table nat --replace PortForwarding 1 " .
"--destination \$OUTERNET --jump $pf_chain\n";
$OUT .= " /sbin/iptables --table nat --flush \$PFC\n";
$OUT .= " /sbin/iptables --table nat --delete-chain \$PFC\n";
}

View File

@@ -0,0 +1,128 @@
#!/usr/bin/perl -wT
# vim: ft=xml ts=4 sw=4 et:
#----------------------------------------------------------------------
# heading : Security
# description : Port forwarding
# navigation : 5000 5400
#----------------------------------------------------------------------
#----------------------------------------------------------------------
# copyright (C) 2002 Mitel Networks Corporation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Technical support for this program is available from Mitel Networks
# Please visit our web site www.mitel.com/sme/ for details.
#----------------------------------------------------------------------
use strict;
use esmith::FormMagick::Panel::portforwarding;
my $form = esmith::FormMagick::Panel::portforwarding->new();
# Uncomment the next line for debugging purposes.
#$form->debug(1);
$form->display();
__DATA__
<form
title="FORM_TITLE"
header="/etc/e-smith/web/common/head.tmpl"
footer="/etc/e-smith/web/common/foot.tmpl">
<!-- page 0 -->
<page
name="First"
pre-event="print_status_message()">
<description>FIRST_PAGE_DESCRIPTION</description>
<subroutine src="show_port_forwards()" />
</page>
<!-- page 1 -->
<page
name="Create"
pre-event="print_status_message()">
<description>CREATE_PAGE_DESCRIPTION</description>
<field
id="protocol"
type="select"
options="'TCP','UDP'">
<label>LABEL_PROTOCOL</label>
</field>
<field
id="source_port"
type="text"
size="11"
validation="validate_source_port()">
<label>LABEL_SOURCE_PORT</label>
</field>
<field
id="destination_host"
type="text"
size="15"
validation="validate_destination_host()">
<label>LABEL_DESTINATION_HOST</label>
</field>
<field
id="destination_port"
type="text"
size="11"
validation="validate_destination_port()">
<label>LABEL_DESTINATION_PORT</label>
</field>
<field
id="rule_comment"
type="text">
<label>LABEL_RULE_COMMENT</label>
</field>
<field
id="allow_hosts"
type="text"
validation="validate_allowed_hosts()">
<label>LABEL_ALLOW_HOSTS</label>
</field>
<subroutine src="print_button('NEXT')" />
</page>
<!-- page 2 -->
<page
name="ShowSummary"
pre-event="turn_off_buttons()"
post-event="create_new()">
<subroutine src="display_summary_create" />
</page>
<!-- page 3
Note: This page is not used. It's a kludge to permit the next page
to work properly from a link on the front page. FormMagick needs
work.
-->
<page
name="Dummy">
</page>
<!-- page 4 -->
<page
name="Remove"
pre-event="turn_off_buttons()"
post-event="remove_rule()">
<subroutine src="display_summary_remove" />
</page>
</form>

View File

@@ -0,0 +1,676 @@
#----------------------------------------------------------------------
# $Id: portforwarding.pm,v 1.38 2005/03/16 23:37:02 charlieb Exp $
# vim: ft=perl ts=4 sw=4 et:
#----------------------------------------------------------------------
# copyright (C) 2002 Mitel Networks Corporation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Technical support for this program is available from Mitel Networks
# Please visit our web site www.e-smith.com for details.
#----------------------------------------------------------------------
package esmith::FormMagick::Panel::portforwarding;
use strict;
use esmith::ConfigDB;
use esmith::FormMagick;
use esmith::util;
use esmith::util::network qw(isValidIP);
use esmith::cgi;
use Exporter;
use constant TRUE => 1;
use constant FALSE => 0;
our @ISA = qw(esmith::FormMagick Exporter);
our @EXPORT = qw(
show_port_forwards create_new validate_source_port
validate_destination_port display_create_summary
);
our $VERSION = sprintf '%d.%03d', q$Revision: 1.38 $ =~ /: (\d+).(\d+)/;
our $db = esmith::ConfigDB->open
|| die "Can't open configuration database: $!\n";
our $tcp_db = esmith::ConfigDB->open('portforward_tcp')
|| die "Can't open portforward_tcp database: $!\n";
our $udp_db = esmith::ConfigDB->open('portforward_udp')
|| die "Can't open portforward_udp database: $!\n";
=head1 NAME
esmith::FormMagick::Panels::portforwarding - useful panel functions
=head1 SYNOPSIS
use esmith::FormMagick::Panels::portforwarding
my $panel = esmith::FormMagick::Panel::portforwarding->new();
$panel->display();
=head1 DESCRIPTION
This module is the backend to the portforwarding panel, responsible for
supplying all functions used by that panel. It is a subclass of
esmith::FormMagick itself, so it inherits the functionality of a FormMagick
object.
=head2 new
This is the class constructor.
=begin testing
$ENV{ESMITH_CONFIG_DB} = "10e-smith-base/configuration.conf";
use_ok('esmith::FormMagick::Panels::portforwarding');
our $panel;
ok($panel = esmith::FormMagick::Panels::portforwarding->new(),
"Create panel object");
isa_ok($panel, 'esmith::FormMagick::Panels::portforwarding');
=end testing
=cut
sub new {
my $class = ref($_[0]) || $_[0];
my $self = esmith::FormMagick->new();
bless $self, $class;
# Uncomment the following line for debugging.
#$self->debug(TRUE);
return $self;
}
=head2 show_port_forwards
This method displays the data on currently forwarded ports on
the system.
=cut
sub show_port_forwards {
my $self = shift;
my $q = $self->cgi;
my $empty = 0;
my @tcpforwards = $tcp_db->get_all;
my @udpforwards = $udp_db->get_all;
$empty = 1 if not @tcpforwards and not @udpforwards;
my %forwards = ();
$forwards{TCP} = \@tcpforwards;
$forwards{UDP} = \@udpforwards;
print $q->Tr(
$q->td({-colspan => 2},
'<br>' .
$q->a({-class => "button-like",
-href => "portforwarding?page=0&page_stack=&Next=Create"},
$self->localise('CREATE_RULE'))));
unless ($empty) {
print $q->Tr(
$q->td({-colspan => 2},
$q->p($self->localise('SHOW_FORWARDS')))),"\n";
my $q = $self->{cgi};
print "<tr><td colspan=\"2\">";
print $q->start_table({-class => 'sme-border'}), "\n ";
print $q->Tr(
esmith::cgi::genSmallCell(
$q,
$self->localise('LABEL_PROTOCOL'),
"header"
), " ",
esmith::cgi::genSmallCell(
$q,
$self->localise('LABEL_SOURCE_PORT'),
"header"
), " ",
esmith::cgi::genSmallCell(
$q,
$self->localise('LABEL_DESTINATION_HOST'),
"header"
), " ",
esmith::cgi::genSmallCell(
$q,
$self->localise('LABEL_DESTINATION_PORT'),
"header",
), " ",
esmith::cgi::genSmallCell(
$q,
$self->localise('ALLOW_HOSTS'),
"header",
), " ",
esmith::cgi::genSmallCell(
$q,
$self->localise('RULE_COMMENT'),
"header",
), " ",
$q->th({-class => "sme-border", -colspan => 2},
$self->localise('ACTION')
), "\n ",
);
foreach my $proto (sort keys %forwards) {
if (@{ $forwards{$proto} }) {
foreach my $entry (@{ $forwards{$proto} }) {
my $sport = $entry->key;
my $dhost = $entry->prop('DestHost');
my $dport = $entry->prop('DestPort') || '';
my $cmmnt = $entry->prop('Comment') || '';
my $allow = $entry->prop('AllowHosts') || '';
print $q->Tr(
esmith::cgi::genSmallCell($q, $proto),
" ",
esmith::cgi::genSmallCell($q, $sport),
" ",
esmith::cgi::genSmallCell($q, $dhost),
" ",
esmith::cgi::genSmallCell($q, $dport || '&nbsp'),
" ",
esmith::cgi::genSmallCell($q, $allow || '&nbsp'),
" ",
esmith::cgi::genSmallCell($q, $cmmnt || '&nbsp'),
" ",
esmith::cgi::genSmallCell(
$q,
$q->a({href => $q->url(-absolute => 1)
. "?page=3&Next=Next&protocol=$proto&"
. "source_port=$sport&"
. "destination_host=$dhost&"
. "destination_port=$dport&"
. "rule_comment=".CGI::escape($cmmnt)."&"
. "allow_hosts=$allow"},
$self->localise("REMOVE"))
),
"\n ",
);
}
}
}
print $q->end_table,"\n";
print '</td></tr>';
}
else {
print $q->Tr(
$q->td({-colspan => 2}, '<br>' .
$self->localise('NO_FORWARDS')));
}
return undef;
}
=head2 validate_source_port
This method validates the source port field in the new port forward page.
=cut
sub validate_source_port {
my $self = shift;
my $q = $self->{cgi};
my $sport = $q->param('source_port');
$sport =~ s/^\s+|\s+$//g;
# If this is a port range, split it up and validate it individually.
my @ports = ();
if ($sport =~ /-/)
{
@ports = split /-/, $sport;
if (@ports > 2)
{
$self->debug_msg("found more than 2 ports: @ports");
return $self->localise('ERR_BADPORT');
}
}
else
{
push @ports, $sport;
}
$self->debug_msg("the ports array is: @ports");
foreach my $port (@ports)
{
$self->debug_msg("looping on port $port");
if (! $self->isValidPort($port))
{
$self->debug_msg("returning: " . $self->localise('ERR_BADPORT'));
return $self->localise('ERR_BADPORT');
}
}
# Now, lets screen any duplicates.
my $protocol = $q->param('protocol');
my @forwards = ();
# Grab the existing rules for this protocol.
if ($protocol eq 'TCP') {
@forwards = map { $_->key } $tcp_db->get_all;
} elsif ($protocol eq 'UDP') {
@forwards = map { $_->key } $udp_db->get_all;
}
foreach my $psport (@forwards)
{
if ($self->detect_collision($sport, $psport))
{
return $self->localise('ERR_PORT_COLLISION');
}
}
return 'OK';
}
=head2 detect_collision
This method looks for a collision between two ports or port ranges.
=cut
sub detect_collision
{
my $self = shift;
my $port_a = shift;
my $port_b = shift;
# If they're both single ports, see if they're the same.
if (($port_a !~ /-/) && ($port_b !~ /-/))
{
return $port_a eq $port_b;
}
# If port_a is not a range but port_b is, is a in b?
elsif ($port_a !~ /-/)
{
my ($b1, $b2) = split /-/, $port_b;
return (($port_a >= $b1) && ($port_a <= $b2));
}
elsif ($port_b !~ /-/)
{
my ($a1, $a2) = split /-/, $port_a;
return (($port_b >= $a1) && ($port_b <= $a2));
}
else
{
# They're both ranges. Do they overlap?
my ($a1, $a2) = split /-/, $port_a;
my ($b1, $b2) = split /-/, $port_b;
# They can overlap in two ways. Either a1 is in b, or b1 is in a.
if (($a1 >= $b1) && ($a1 <= $b2))
{
return TRUE;
}
elsif (($b1 >= $a1) && ($b1 <= $a2))
{
return TRUE;
}
return FALSE;
}
}
=head2 validate_destination_port
This method validates the destination port field in the new port
forward page.
=cut
sub validate_destination_port {
my $self = shift;
my $dport = $self->{cgi}->param('destination_port');
$dport =~ s/^\s+|\s+$//g;
# If the dport is empty, that's ok.
return 'OK' if not $dport;
# If this is a port range, split it up and validate it individually.
my @ports = ();
if ($dport =~ /-/)
{
@ports = split /-/, $dport;
if (@ports > 2)
{
$self->debug_msg("found more than 2 ports: @ports");
return $self->localise('ERR_BADPORT');
}
}
else
{
push @ports, $dport;
}
$self->debug_msg("the ports array is: @ports");
foreach my $port (@ports)
{
$self->debug_msg("looping on port $port");
if (! $self->isValidPort($port))
{
$self->debug_msg("returning: " . $self->localise('ERR_BADPORT'));
return $self->localise('ERR_BADPORT');
}
}
return 'OK';
}
=head2 isValidPort
Test for a valid port.
FIXME: Remove this when 5.6 is no longer supported, and use
esmith::util::network::isValidPort instead.
=begin testing
@badports = (98765434, -183, 0, 'bad port', 'a');
@goodports = (67, 23, 1, 54736);
foreach $port (@badports) {
$panel->{cgi}->param('destination_port' => $port);
isnt($panel->validate_source_port(), "OK");
}
foreach $port (@goodports) {
$panel->{cgi}->param('source_port' => $port);
is($panel->validate_source_port(), "OK");
}
=end testing
=cut
sub isValidPort() {
my $self = shift;
my $port = shift;
return FALSE unless defined $port;
if (($port =~ /^\d+$/) &&
($port > 0) &&
($port < 65536))
{
return TRUE;
}
else {
return FALSE;
}
}
=head2 validate_destination_host
The purpose of this method is to validate the destination host field in the
new port forward page.
=cut
sub validate_destination_host {
my $self = shift;
my $dhost = $self->{cgi}->param('destination_host');
$dhost =~ s/^\s+|\s+$//g;
my $localip = $db->get_prop('InternalInterface', 'IPAddress');
my $external_ip = $db->get_prop('ExternalInterface', 'IPAddress') || $localip;
if ($dhost =~ /^(localhost|127.0.0.1|$localip|$external_ip)$/i)
{
# localhost token gets expanded at runtime to current external IP
$self->{cgi}->param(-name=>'destination_host', -value=>'localhost');
return "OK";
}
my $systemmode = $db->get_value('SystemMode');
if ($systemmode eq 'serveronly') {
return $self->localise('IN_SERVERONLY');
}
if (isValidIP($dhost)) {
return 'OK';
}
else {
return $self->localise('ERR_BADIP');
}
}
=head2 validate_allowed_hosts
=cut
sub validate_allowed_hosts {
my $self = shift;
my $ahost = $self->{cgi}->param('allow_hosts');
$ahost =~ s/^\s+|\s+$//g;
my $valid_ahost_list = "OK";
foreach (split(/[\s,]+/, $ahost)) {
my $valid_ipnet = 0;
$valid_ipnet = 1 if ($_ =~ m/^\d+\.\d+\.\d+\.\d+$/);
$valid_ipnet = 1 if ($_ =~ m/^\d+\.\d+\.\d+\.\d+\/\d+$/);
$valid_ahost_list = "ERR_BADAHOST" if ($valid_ipnet != 1);
}
return $valid_ahost_list;
}
=head2 display_summary_create
This is a wrapper for the display_summary method, to call it in create mode.
=cut
sub display_summary_create {
my $self = shift;
$self->display_summary('create');
}
=head2 display_summary_remove
This is a wrapper for the display_summary method, to call it in remove mode.
=cut
sub display_summary_remove {
my $self = shift;
$self->display_summary('remove');
}
=head2 display_create_summary
This method's purpose is to display a summary of the rule about to be added.
=cut
sub display_summary {
my $self = shift;
my $mode = shift;
my $save = $self->localise('SAVE');
my $cancel = $self->localise('CANCEL');
my $output = "";
my $q = $self->{cgi};
$self->debug_msg("start of method");
print "<tr><td colspan=\"2\">";
my $description = "";
if ($mode eq 'create') {
$description = $self->localise('SUMMARY_ADD_DESC');
}
elsif ($mode eq 'remove') {
$description = $self->localise('SUMMARY_REMOVE_DESC');
}
else {
return $self->error('ERR_UNSUPPORTED_MODE');
}
print $q->p($description);
my $dhost = $self->get_destination_host();
foreach my $tablearrayref (
[$self->localise('LABEL_PROTOCOL')
=> $q->param('protocol')],
[$self->localise('LABEL_SOURCE_PORT')
=> $q->param('source_port')],
[$self->localise('LABEL_DESTINATION_PORT')
=> $q->param('destination_port') || '&nbsp;'],
[$self->localise('LABEL_DESTINATION_HOST')
=> $dhost],
[$self->localise('RULE_COMMENT')
=> $q->param('rule_comment')],
[$self->localise('ALLOW_HOSTS')
=> $q->param('allow_hosts')],
)
{
print $q->Tr(
$q->td({-class => 'sme-noborders-label'},
$tablearrayref->[0],
$q->td({-class => 'sme-noborders-content'},
$tablearrayref->[1]))), "\n";
}
if ($mode eq 'create') {
print $q->table({-width => '100%'}, $q->Tr($q->th({-class => 'sme-layout'},
$q->submit(-name => 'apply',
-value => $self->localise("ADD")),
'&nbsp;',
$q->submit(-name => 'cancel',
-value => $self->localise("CANCEL")))));
}
elsif ($mode eq 'remove') {
print $q->table({-width => '100%'}, $q->Tr($q->th({-class => 'sme-layout'},
$q->submit( -name => 'remove',
-value => $self->localise("REMOVE")),
'&nbsp;',
$q->submit( -name => 'cancel',
-value => $self->localise("CANCEL")))));
}
else {
return $self->error('ERR_UNSUPPORTED_MODE');
}
$self->debug_msg("returning");
print "</td></tr>";
return undef;
}
=head2 remove_rule
This method is a remove wrapper for the modify method.
=cut
sub remove_rule {
my $self = shift;
$self->modify('remove');
}
=head2 create_new
This method is a create wrapper for the modify method.
=cut
sub create_new {
my $self = shift;
$self->modify('create');
}
=head2 modify
This method's purpose is to add or remove rules from the database, and then
cause the firewall rules to update.
=cut
sub modify {
no strict 'refs';
my $self = shift;
my $mode = shift;
my $q = $self->{cgi};
$self->debug_msg("at start of modify method");
# If the cancel button was pressed, just go back to the start page.
if ($q->param("cancel")) {
$self->debug_msg("the cancel button was pressed");
$self->wherenext("First");
}
else {
# Save the changes.
my $proto = $q->param("protocol");
my $sport = $q->param("source_port");
my $dport = $q->param("destination_port");
my $dhost = $self->get_destination_host();
my $cmmnt = $q->param("rule_comment") || "";
my $allow = $q->param("allow_hosts") || "";
my $deny = (($q->param("allow_hosts")) ? "0.0.0.0/0" : "");
$proto =~ s/^\s+|\s+$//g;
$sport =~ s/^\s+|\s+$//g;
$dport =~ s/^\s+|\s+$//g;
$dhost =~ s/^\s+|\s+$//g;
$self->debug_msg("protocol is $proto");
$self->debug_msg("source_port is $sport");
$self->debug_msg("destination_port is $dport");
$self->debug_msg("destination_host is $dhost");
my $whichforwards = "";
my $fdb;
if ($proto eq 'TCP') {
$fdb = $tcp_db;
}
else {
$fdb = $udp_db;
}
if ($mode eq 'create') {
$self->debug_msg("we are in create mode");
my $entry = $fdb->get($sport) || $fdb->new_record($sport, { type => 'forward' });
$entry->set_prop('DestHost', $dhost);
$entry->set_prop('DestPort', $dport) if $dport;
$entry->set_prop('Comment', $cmmnt);
$entry->set_prop('AllowHosts', $allow);
$entry->set_prop('DenyHosts', $deny);
}
elsif ($mode eq 'remove') {
$self->debug_msg("we are in remove mode");
my $entry = $fdb->get($sport);
return $self->error('ERR_CANNOT_REMOVE_NORULE') unless $entry;
$entry->delete;
}
system("/sbin/e-smith/signal-event",
"portforwarding-update") == 0
|| return $self->error('ERR_NONZERO_RETURN_EVENT');
return $self->success();
}
}
=head2 get_destination_host
Get the 'destination_host' parameter, and fold it to 'localhost' if it
matches any local interface IP address.
=cut
sub get_destination_host
{
my $self = shift;
my $q = $self->{cgi};
my $dhost = $q->param("destination_host");
my $localip = $db->get_prop('InternalInterface', 'IPAddress');
my $external_ip = $db->get_prop('ExternalInterface', 'IPAddress') || $localip;
if ($dhost =~ /^(127.0.0.1|$localip|$external_ip)$/i)
{
# localhost token gets expanded at runtime to current external IP
$dhost = 'localhost';
}
return $dhost;
}
1;