initial commit of file from CVS for e-smith-portforwarding on Wed 12 Jul 09:04:13 BST 2023
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
168
root/etc/e-smith/locale/en-us/etc/e-smith/web/functions/portforwarding
Executable file
168
root/etc/e-smith/locale/en-us/etc/e-smith/web/functions/portforwarding
Executable 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 "Add" button. If you are not, click the
|
||||
"Cancel" 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 "Remove" button. If not,
|
||||
click the "Cancel" 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
|
||||
"Remove" 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>
|
@@ -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
|
@@ -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";
|
||||
}
|
128
root/etc/e-smith/web/functions/portforwarding
Executable file
128
root/etc/e-smith/web/functions/portforwarding
Executable 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>
|
@@ -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 || ' '),
|
||||
" ",
|
||||
esmith::cgi::genSmallCell($q, $allow || ' '),
|
||||
" ",
|
||||
esmith::cgi::genSmallCell($q, $cmmnt || ' '),
|
||||
" ",
|
||||
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') || ' '],
|
||||
[$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")),
|
||||
' ',
|
||||
$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")),
|
||||
' ',
|
||||
$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;
|
Reference in New Issue
Block a user