#!/usr/bin/perl # # SmoothWall CGIs # # This code is distributed under the terms of the GPL # # (c) The SmoothWall Team # Copyright (c) 2002/04/13 Steve Bootes - Add source IP support # # $Id: portfw.cgi,v 1.5.2.18 2005/05/02 16:19:49 eoberlander Exp $ # # # Darren Critchley February 2003 - I added the multiple external access rules for each port forward # A couple of things to remember when reading the code # There are two kinds of records in the config file, those with a number in the first field, and then 0, # these are port forward rules, these records will have a 0 or 0.0.0.0 in position 9 (ORIG_IP) # If there is a 0, it means that there are external access rules, otherwise the port is open to ALL. # The second type of record is a number followed by a number which indicates that it is an external access # rule. The first number indicates which Portfw rule it belongs to, and the second is just a unique key. # # Darren Critchley - March 5, 2003 - if you come along after me and work on this page, please comment your # work. Put your name, and date and then your comment - it helps the person that comes along after you # to figure out why and how things have changed, and it is considered good coding practice # Thanks . . . # use strict; # enable only the following on debugging purpose #use warnings; #use CGI::Carp 'fatalsToBrowser'; require '/var/ipfire/general-functions.pl'; require "${General::swroot}/lang.pl"; require "${General::swroot}/header.pl"; #workaround to suppress a warning when a variable is used only once my @dummy = ( ${Header::colouryellow} ); undef (@dummy); my %cgiparams=(); my %selected=(); my %checked=(); my $prtrange1=0; my $prtrange2=0; my $errormessage = ''; my $filename = "${General::swroot}/portfw/config"; my $aliasfile = "${General::swroot}/ethernet/aliases"; &Header::showhttpheaders(); $cgiparams{'ENABLED'} = 'off'; $cgiparams{'KEY1'} = '0'; $cgiparams{'KEY2'} = '0'; $cgiparams{'PROTOCOL'} = ''; $cgiparams{'SRC_PORT'} = ''; $cgiparams{'DEST_IP'} = ''; $cgiparams{'DEST_PORT'} = ''; $cgiparams{'SRC_IP'} = ''; $cgiparams{'ORIG_IP'} = ''; $cgiparams{'REMARK'} = ''; $cgiparams{'OVERRIDE'} = 'off'; $cgiparams{'ACTION'} = ''; &Header::getcgihash(\%cgiparams); my $disable_all = "0"; my $enable_all = "0"; if ($cgiparams{'ACTION'} eq $Lang::tr{'add'}) { &valaddupdate(); # Darren Critchley - if there is an error, don't waste any more time processing if ($errormessage) { goto ERROR; } open(FILE, $filename) or die 'Unable to open config file.'; my @current = ; close(FILE); my $key1 = 0; # used for finding last sequence number used foreach my $line (@current) { my @temp = split(/\,/,$line); chomp ($temp[8]); if ($cgiparams{'KEY2'} eq "0"){ # if key2 is 0 then it is a portfw addition if ( $cgiparams{'SRC_PORT'} eq $temp[3] && $cgiparams{'PROTOCOL'} eq $temp[2] && $cgiparams{'SRC_IP'} eq $temp[7]) { $errormessage = "$Lang::tr{'source port in use'} $cgiparams{'SRC_PORT'}"; } # Check if key2 = 0, if it is then it is a port forward entry and we want the sequence number if ( $temp[1] eq "0") { $key1=$temp[0]; } # Darren Critchley - Duplicate or overlapping Port range check if ($temp[1] eq "0" && $cgiparams{'PROTOCOL'} eq $temp[2] && $cgiparams{'SRC_IP'} eq $temp[7] && $errormessage eq '') { &portchecks($temp[3], $temp[5]); } } else { if ( $cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'ORIG_IP'} eq $temp[8]) { $errormessage = "$Lang::tr{'source ip in use'} $cgiparams{'ORIG_IP'}"; } } } ERROR: unless ($errormessage) { # Darren Critchley - we only want to store ranges with Colons $cgiparams{'SRC_PORT'} =~ tr/-/:/; $cgiparams{'DEST_PORT'} =~ tr/-/:/; if ($cgiparams{'KEY1'} eq "0") { # 0 in KEY1 indicates it is a portfw add $key1++; # Add one to last sequence number open(FILE,">>$filename") or die 'Unable to open config file.'; flock FILE, 2; if ($cgiparams{'ORIG_IP'} eq '0.0.0.0/0') { # if the default/all is taken, then write it to the rule print FILE "$key1,0,$cgiparams{'PROTOCOL'},$cgiparams{'SRC_PORT'},$cgiparams{'DEST_IP'},$cgiparams{'DEST_PORT'},$cgiparams{'ENABLED'},$cgiparams{'SRC_IP'},$cgiparams{'ORIG_IP'},$cgiparams{'REMARK'}\n"; } else { # else create an extra record so it shows up print FILE "$key1,0,$cgiparams{'PROTOCOL'},$cgiparams{'SRC_PORT'},$cgiparams{'DEST_IP'},$cgiparams{'DEST_PORT'},$cgiparams{'ENABLED'},$cgiparams{'SRC_IP'},0,$cgiparams{'REMARK'}\n"; print FILE "$key1,1,$cgiparams{'PROTOCOL'},0,$cgiparams{'DEST_IP'},$cgiparams{'DEST_PORT'},$cgiparams{'ENABLED'},0,$cgiparams{'ORIG_IP'},$cgiparams{'REMARK'}\n"; } close(FILE); undef %cgiparams; &General::log($Lang::tr{'forwarding rule added'}); system('/usr/local/bin/setportfw'); } else { # else key1 eq 0 my $insertpoint = ($cgiparams{'KEY2'} - 1); open(FILE, ">$filename") or die 'Unable to open config file.'; flock FILE, 2; foreach my $line (@current) { chomp($line); my @temp = split(/\,/,$line); if ($cgiparams{'KEY1'} eq $temp[0] && $insertpoint eq $temp[1]) { if ($temp[1] eq "0") { # this is the first xtaccess rule, therefore modify the portfw rule $temp[8] = '0'; } print FILE "$temp[0],$temp[1],$temp[2],$temp[3],$temp[4],$temp[5],$temp[6],$temp[7],$temp[8],$temp[9]\n"; print FILE "$cgiparams{'KEY1'},$cgiparams{'KEY2'},$cgiparams{'PROTOCOL'},0,$cgiparams{'DEST_IP'},$cgiparams{'DEST_PORT'},$cgiparams{'ENABLED'},0,$cgiparams{'ORIG_IP'},$cgiparams{'REMARK'}\n"; } else { print FILE "$line\n"; } } close(FILE); undef %cgiparams; &General::log($Lang::tr{'external access rule added'}); system('/usr/local/bin/setportfw'); } # end if if KEY1 eq 0 } # end unless($errormessage) } if ($cgiparams{'ACTION'} eq $Lang::tr{'update'}) { &valaddupdate(); # Darren Critchley - If there is an error don't waste any more processing time if ($errormessage) { $cgiparams{'ACTION'} = $Lang::tr{'edit'}; goto UPD_ERROR; } open(FILE, $filename) or die 'Unable to open config file.'; my @current = ; close(FILE); my $disabledpfw = '0'; my $lastpfw = ''; my $xtaccessdel = '0'; foreach my $line (@current) { my @temp = split(/\,/,$line); if ( $temp[1] eq "0" ) { # keep track of the last portfw and if it is enabled $disabledpfw = $temp[6]; $lastpfw = $temp[0]; } chomp ($temp[8]); if ( $cgiparams{'SRC_PORT'} eq $temp[3] && $cgiparams{'PROTOCOL'} eq $temp[2] && $cgiparams{'SRC_IP'} eq $temp[7]) { if ($cgiparams{'KEY1'} ne $temp[0] && $cgiparams{'KEY2'} eq "0") { $errormessage = "$Lang::tr{'source port in use'} $cgiparams{'SRC_PORT'}"; } } if ($cgiparams{'ORIG_IP'} eq $temp[8]) { if ($cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'KEY2'} ne $temp[1]) # If we have the same source ip within a portfw group, then we have a problem! { $errormessage = "$Lang::tr{'source ip in use'} $cgiparams{'ORIG_IP'}"; $cgiparams{'ACTION'} = $Lang::tr{'edit'}; } } # Darren Critchley - Flag when a user disables an xtaccess if ($cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'KEY2'} eq $temp[1] && $cgiparams{'KEY2'} ne "0" && # if KEY2 is 0 then it is a portfw $cgiparams{'ENABLED'} eq "off" && $temp[6] eq "on") { # we have determined that someone has turned an xtaccess off $xtaccessdel = "1"; } # Darren Critchley - Portfw enabled, then enable xtaccess for all associated xtaccess records if ($cgiparams{'ENABLED'} eq "on" && $cgiparams{'KEY2'} eq "0" && $cgiparams{'ENABLED'} ne $temp[6]) { $enable_all = "1"; } else { $enable_all = "0"; } # Darren Critchley - Portfw disabled, then disable xtaccess for all associated xtaccess records if ($cgiparams{'ENABLED'} eq "off" && $cgiparams{'KEY2'} eq "0") { $disable_all = "1"; } else { $disable_all = "0"; } # Darren Critchley - if we are enabling an xtaccess, only allow if the associated Portfw is enabled if ($cgiparams{'KEY1'} eq $lastpfw && $cgiparams{'KEY2'} ne "0") { # identifies an xtaccess record in the group if ($cgiparams{'ENABLED'} eq "on" && $cgiparams{'ENABLED'} ne $temp[6] ){ # a change has been made if ($disabledpfw eq "off") { $errormessage = "$Lang::tr{'cant enable xtaccess'}"; $cgiparams{'ACTION'} = $Lang::tr{'edit'}; } } } # Darren Critchley - rule to stop someone from entering ALL into a external access rule, # the portfw is the only place that ALL can be specified if ($cgiparams{'KEY2'} ne "0" && $cgiparams{'ORIG_IP'} eq "0.0.0.0/0") { $errormessage = "$Lang::tr{'xtaccess all error'}"; $cgiparams{'ACTION'} = $Lang::tr{'edit'}; } # Darren Critchley - Duplicate or overlapping Port range check if ($temp[1] eq "0" && $cgiparams{'KEY1'} ne $temp[0] && $cgiparams{'PROTOCOL'} eq $temp[2] && $cgiparams{'SRC_IP'} eq $temp[7] && $errormessage eq '') { &portchecks($temp[3], $temp[5]); } # end port testing } # Darren Critchley - if an xtaccess was disabled, now we need to check to see if it was the only xtaccess if($xtaccessdel eq "1") { my $xctr = 0; foreach my $line (@current) { my @temp = split(/\,/,$line); if($temp[0] eq $cgiparams{'KEY1'} && $temp[6] eq "on") { # we only want to count the enabled xtaccess's $xctr++; } } if ($xctr == 2){ $disable_all = "1"; } } UPD_ERROR: unless ($errormessage) { # Darren Critchley - we only want to store ranges with Colons $cgiparams{'SRC_PORT'} =~ tr/-/:/; $cgiparams{'DEST_PORT'} =~ tr/-/:/; open(FILE, ">$filename") or die 'Unable to open config file.'; flock FILE, 2; foreach my $line (@current) { chomp($line); my @temp = split(/\,/,$line); if ($cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'KEY2'} eq $temp[1]) { print FILE "$cgiparams{'KEY1'},$cgiparams{'KEY2'},$cgiparams{'PROTOCOL'},$cgiparams{'SRC_PORT'},$cgiparams{'DEST_IP'},$cgiparams{'DEST_PORT'},$cgiparams{'ENABLED'},$cgiparams{'SRC_IP'},$cgiparams{'ORIG_IP'},$cgiparams{'REMARK'}\n"; } else { # Darren Critchley - If it is a port forward record, then chances are good that a change was made to # Destination Ip or Port, and we need to update all the associated external access records if ($cgiparams{'KEY2'} eq "0" && $cgiparams{'KEY1'} eq $temp[0]) { $temp[4] = $cgiparams{'DEST_IP'}; $temp[5] = $cgiparams{'DEST_PORT'}; $temp[2] = $cgiparams{'PROTOCOL'}; } # Darren Critchley - If a Portfw has been disabled, then set all associated xtaccess as disabled if ( $disable_all eq "1" && $cgiparams{'KEY1'} eq $temp[0] ) { $temp[6] = 'off'; } if ( $enable_all eq "1" && $cgiparams{'KEY1'} eq $temp[0] ) { $temp[6] = 'on'; } # Darren Critchley - Deal with the override to allow ALL if ( $cgiparams{'OVERRIDE'} eq "on" && $temp[1] ne "0" && $cgiparams{'KEY1'} eq $temp[0] ) { $temp[6] = 'off'; } print FILE "$temp[0],$temp[1],$temp[2],$temp[3],$temp[4],$temp[5],$temp[6],$temp[7],$temp[8],$temp[9]\n"; } } close(FILE); undef %cgiparams; &General::log($Lang::tr{'forwarding rule updated'}); system('/usr/local/bin/setportfw'); } if ($errormessage) { $cgiparams{'ACTION'} = $Lang::tr{'edit'}; } } # Darren Critchley - Allows rules to be enabled and disabled if ($cgiparams{'ACTION'} eq $Lang::tr{'toggle enable disable'}) { open(FILE, $filename) or die 'Unable to open config file.'; my @current = ; close(FILE); my $disabledpfw = '0'; my $lastpfw = ''; my $xtaccessdel = '0'; foreach my $line (@current) { my @temp = split(/\,/,$line); if ( $temp[1] eq "0" ) { # keep track of the last portfw and if it is enabled $disabledpfw = $temp[6]; $lastpfw = $temp[0]; } # Darren Critchley - Flag when a user disables an xtaccess if ($cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'KEY2'} eq $temp[1] && $cgiparams{'KEY2'} ne "0" && # if KEY2 is 0 then it is a portfw $cgiparams{'ENABLED'} eq "off" && $temp[6] eq "on") { # we have determined that someone has turned an xtaccess off $xtaccessdel = "1"; } # Darren Critchley - Portfw enabled, then enable xtaccess for all associated xtaccess records if ($cgiparams{'ENABLED'} eq "on" && $cgiparams{'KEY2'} eq "0" && $cgiparams{'ENABLED'} ne $temp[6]) { $enable_all = "1"; } else { $enable_all = "0"; } # Darren Critchley - Portfw disabled, then disable xtaccess for all associated xtaccess records if ($cgiparams{'ENABLED'} eq "off" && $cgiparams{'KEY2'} eq "0") { $disable_all = "1"; } else { $disable_all = "0"; } # Darren Critchley - if we are enabling an xtaccess, only allow if the associated Portfw is enabled if ($cgiparams{'KEY1'} eq $lastpfw && $cgiparams{'KEY2'} ne "0") { # identifies an xtaccess record in the group if ($cgiparams{'ENABLED'} eq "on" && $cgiparams{'ENABLED'} ne $temp[6] ){ # a change has been made if ($disabledpfw eq "off") { $errormessage = "$Lang::tr{'cant enable xtaccess'}"; goto TOGGLEEXIT; } } } } # Darren Critchley - if an xtaccess was disabled, now we need to check to see if it was the only xtaccess if($xtaccessdel eq "1") { my $xctr = 0; foreach my $line (@current) { my @temp = split(/\,/,$line); if($temp[0] eq $cgiparams{'KEY1'} && $temp[6] eq "on") { # we only want to count the enabled xtaccess's $xctr++; } } if ($xctr == 2){ $disable_all = "1"; } } open(FILE, ">$filename") or die 'Unable to open config file.'; flock FILE, 2; foreach my $line (@current) { chomp($line); my @temp = split(/\,/,$line); if ($cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'KEY2'} eq $temp[1]) { print FILE "$cgiparams{'KEY1'},$cgiparams{'KEY2'},$temp[2],$temp[3],$temp[4],$temp[5],$cgiparams{'ENABLED'},$temp[7],$temp[8],$temp[9]\n"; } else { # Darren Critchley - If a Portfw has been disabled, then set all associated xtaccess as disabled if ( $disable_all eq "1" && $cgiparams{'KEY1'} eq $temp[0] ) { $temp[6] = 'off'; } if ( $enable_all eq "1" && $cgiparams{'KEY1'} eq $temp[0] ) { $temp[6] = 'on'; } print FILE "$temp[0],$temp[1],$temp[2],$temp[3],$temp[4],$temp[5],$temp[6],$temp[7],$temp[8],$temp[9]\n"; } } close(FILE); &General::log($Lang::tr{'forwarding rule updated'}); system('/usr/local/bin/setportfw'); TOGGLEEXIT: undef %cgiparams; } # Darren Critchley - broke out Edit routine from the delete routine - Edit routine now just puts values in fields if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'}) { open(FILE, "$filename") or die 'Unable to open config file.'; my @current = ; close(FILE); unless ($errormessage) { foreach my $line (@current) { chomp($line); my @temp = split(/\,/,$line); if ($cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'KEY2'} eq $temp[1] ) { $cgiparams{'PROTOCOL'} = $temp[2]; $cgiparams{'SRC_PORT'} = $temp[3]; $cgiparams{'DEST_IP'} = $temp[4]; $cgiparams{'DEST_PORT'} = $temp[5]; $cgiparams{'ENABLED'} = $temp[6]; $cgiparams{'SRC_IP'} = $temp[7]; $cgiparams{'ORIG_IP'} = $temp[8]; $cgiparams{'REMARK'} = $temp[9]; } } } } # Darren Critchley - broke out Remove routine as the logic is getting too complex to be combined with the Edit if ($cgiparams{'ACTION'} eq $Lang::tr{'remove'}) { open(FILE, "$filename") or die 'Unable to open config file.'; my @current = ; close(FILE); # If the record being deleted is an xtaccess record, and it is the only one for a portfw record # then we need to adjust the portfw record to be open to ALL ip addressess or an error will occur # in setportfw.c my $fixportfw = '0'; if ($cgiparams{'KEY2'} ne "0") { my $counter = 0; foreach my $line (@current) { chomp($line); my @temp = split(/\,/,$line); if ($temp[0] eq $cgiparams{'KEY1'}) { $counter++; } } if ($counter eq 2) { $fixportfw = '1'; } } unless ($errormessage) { open(FILE, ">$filename") or die 'Unable to open config file.'; flock FILE, 2; my $linedeleted = 0; foreach my $line (@current) { chomp($line); my @temp = split(/\,/,$line); if ($cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'KEY2'} eq $temp[1] || $cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'KEY2'} eq "0" ) { $linedeleted = 1; } else { if ($temp[0] eq $cgiparams{'KEY1'} && $temp[1] eq "0" && $fixportfw eq "1") { $temp[8] = '0.0.0.0/0'; } print FILE "$temp[0],$temp[1],$temp[2],$temp[3],$temp[4],$temp[5],$temp[6],$temp[7],$temp[8],$temp[9]\n"; # print FILE "$line\n"; } } close(FILE); if ($linedeleted == 1) { &General::log($Lang::tr{'forwarding rule removed'}); undef %cgiparams; } system('/usr/local/bin/setportfw'); } } # Darren Critchley - Added routine to allow external access rules to be added if ($cgiparams{'ACTION'} eq $Lang::tr{'add xtaccess'}) { open(FILE, $filename) or die 'Unable to open config file.'; my @current = ; close(FILE); my $key = 0; # used for finding last sequence number used foreach my $line (@current) { my @temp = split(/\,/,$line); if ($temp[0] eq $cgiparams{'KEY1'}) { $key = $temp[1] } if ($cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'KEY2'} eq $temp[1] ) { $cgiparams{'PROTOCOL'} = $temp[2]; $cgiparams{'SRC_PORT'} = $temp[3]; $cgiparams{'DEST_IP'} = $temp[4]; $cgiparams{'DEST_PORT'} = $temp[5]; $cgiparams{'ENABLED'} = $temp[6]; $cgiparams{'SRC_IP'} = $temp[7]; $cgiparams{'ORIG_IP'} = ''; $cgiparams{'REMARK'} = $temp[9]; } } $key++; $cgiparams{'KEY2'} = $key; # Until the ADD button is hit, there needs to be no change to portfw rules } if ($cgiparams{'ACTION'} eq $Lang::tr{'reset'}) { undef %cgiparams; } if ($cgiparams{'ACTION'} eq '') { $cgiparams{'PROTOCOL'} = 'tcp'; $cgiparams{'ENABLED'} = 'on'; $cgiparams{'SRC_IP'} = '0.0.0.0'; } $selected{'PROTOCOL'}{'udp'} = ''; $selected{'PROTOCOL'}{'tcp'} = ''; $selected{'PROTOCOL'}{'gre'} = ''; $selected{'PROTOCOL'}{$cgiparams{'PROTOCOL'}} = "selected='selected'"; $selected{'SRC_IP'}{$cgiparams{'SRC_IP'}} = "selected='selected'"; $checked{'ENABLED'}{'off'} = ''; $checked{'ENABLED'}{'on'} = ''; $checked{'ENABLED'}{$cgiparams{'ENABLED'}} = "checked='checked'"; &Header::openpage($Lang::tr{'port forwarding configuration'}, 1, ''); &Header::openbigbox('100%', 'left', '', $errormessage); if ($errormessage) { &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); print "$errormessage\n"; print " \n"; &Header::closebox(); } print "
\n"; if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'}){ &Header::openbox('100%', 'left', $Lang::tr{'edit a rule'}); } else { &Header::openbox('100%', 'left', $Lang::tr{'add a new rule'}); } if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'} && $cgiparams{'KEY2'} ne "0" || $cgiparams{'ACTION'} eq $Lang::tr{'add xtaccess'}){ # if it is not a port forward record, don't validate as the fields are disabled my $PROT = "\U$cgiparams{'PROTOCOL'}\E"; # Darren Critchley - Format the source and destination ports my $dstprt = $cgiparams{'DEST_PORT'}; $dstprt =~ s/-/ - /; $dstprt =~ s/:/ - /; print < $Lang::tr{'protocol'}: $PROT   $Lang::tr{'destination ip'}:  $cgiparams{'DEST_IP'}   $Lang::tr{'destination port'}:  $dstprt END ; } else { print < $Lang::tr{'protocol'}:  $Lang::tr{'alias ip'}:     $Lang::tr{'destination ip'}: $Lang::tr{'destination port'}: END ; } print < $Lang::tr{'remark title'} *  END ; unless ($cgiparams{'ACTION'} eq $Lang::tr{'add xtaccess'} && $cgiparams{'ENABLED'} eq "off") { print " "; print "$Lang::tr{'enabled'} \n"; } print < END ; if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'} && $cgiparams{'KEY2'} eq "0" && ($cgiparams{'ORIG_IP'} eq "0" || $cgiparams{'ORIG_IP'} eq "0.0.0.0/0")){ # if it is a port forward rule with a 0 in the orig_port field, this means there are xtaccess records, and we # don't want to allow a person to change the orig_ip field as it will mess other logic up print "\n"; } else { print < $Lang::tr{'source network'} *  END ; } print <
* $Lang::tr{'this field may be blank'} END ; if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'}){ if($cgiparams{'KEY2'} eq "0"){ print "$Lang::tr{'open to all'}: \n"; } else { print " \n"; } print ""; print ""; print ""; print ""; # on an edit and an xtaccess add, for some reason the "Reset" button stops working, so I make it a submit button } else { print " \n"; print ""; if ($cgiparams{'ACTION'} eq $Lang::tr{'add xtaccess'}) { print ""; print ""; print ""; } elsif ($errormessage ne '') { print ""; } else { print ""; } } print <  END ; &Header::closebox(); print "\n"; &Header::openbox('100%', 'left', $Lang::tr{'current rules'}); print < $Lang::tr{'proto'} $Lang::tr{'source'}   $Lang::tr{'destination'} $Lang::tr{'remark'} $Lang::tr{'action'} END ; my $id = 0; my $xtaccesscolor = '#F6F4F4'; open(RULES, "$filename") or die 'Unable to open config file.'; while () { my $protocol = ''; my $gif = ''; my $gdesc = ''; my $toggle = ''; chomp($_); my @temp = split(/\,/,$_); $temp[9] ='' unless defined $temp[9];# Glles ESpinasse : suppress warning on page init if ($temp[2] eq 'udp') { $protocol = 'UDP'; } elsif ($temp[2] eq 'gre') { $protocol = 'GRE' } else { $protocol = 'TCP' } # Change bgcolor when a new portfw rule is added if ($temp[1] eq "0"){ $id++; } # Darren Critchley highlight the row we are editing if ( $cgiparams{'ACTION'} eq $Lang::tr{'edit'} && $cgiparams{'KEY1'} eq $temp[0] && $cgiparams{'KEY2'} eq $temp[1] ) { print "\n"; } else { if ($id % 2) { print "\n"; } else { print "\n"; } } if ($temp[6] eq 'on') { $gif = 'on.gif'; $toggle='off'; $gdesc=$Lang::tr{'click to disable'};} else { $gif = 'off.gif'; $toggle='on'; $gdesc=$Lang::tr{'click to enable'}; } # Darren Critchley - this code no longer works - should we remove? # catch for 'old-style' rules file - assume default ip if # none exists if (!&General::validip($temp[7]) || $temp[7] eq '0.0.0.0') { $temp[7] = 'DEFAULT IP'; } if ($temp[1] eq '0') { # Port forwarding entry # Darren Critchley - Format the source and destintation ports my $srcprt = $temp[3]; $srcprt =~ s/-/ - /; $srcprt =~ s/:/ - /; my $dstprt = $temp[5]; $dstprt =~ s/-/ - /; $dstprt =~ s/:/ - /; # Darren Critchley - Get Port Service Name if we can - code borrowed from firewalllog.dat $_=$temp[3]; if (/^\d+$/) { my $servi = uc(getservbyport($temp[3], lc($temp[2]))); if ($servi ne '' && $temp[3] < 1024) { $srcprt = "$srcprt($servi)"; } } $_=$temp[5]; if (/^\d+$/) { my $servi = uc(getservbyport($temp[5], lc($temp[2]))); if ($servi ne '' && $temp[5] < 1024) { $dstprt = "$dstprt($servi)"; } } # Darren Critchley - If the line is too long, wrap the port numbers my $srcaddr = "$temp[7] : $srcprt"; if (length($srcaddr) > 22) { $srcaddr = "$temp[7] :
$srcprt"; } my $dstaddr = "$temp[4] : $dstprt"; if (length($dstaddr) > 26) { $dstaddr = "$temp[4] :
$dstprt"; } print <$protocol $srcaddr => $dstaddr  $temp[9]
END ; } else { # external access entry print <   $Lang::tr{'access allowed'} $temp[8]     ($temp[9])
 
END ; } } close(RULES); print ""; # If the fixed lease file contains entries, print Key to action icons if ( ! -z "$filename") { print <  $Lang::tr{'legend'}:  $Lang::tr{ $Lang::tr{'click to disable'}    $Lang::tr{ $Lang::tr{'click to enable'}    $Lang::tr{ $Lang::tr{'add xtaccess'}    $Lang::tr{ $Lang::tr{'edit'}    $Lang::tr{ $Lang::tr{'remove'} END ; } &Header::closebox(); &Header::closebigbox(); &Header::closepage(); # Validate Field Entries sub validateparams { # Darren Critchley - Get rid of dashes in port ranges $cgiparams{'DEST_PORT'}=~ tr/-/:/; $cgiparams{'SRC_PORT'}=~ tr/-/:/; # Darren Critchley - code to substitue wildcards if ($cgiparams{'SRC_PORT'} eq "*") { $cgiparams{'SRC_PORT'} = "1:65535"; } if ($cgiparams{'SRC_PORT'} =~ /^(\D)\:(\d+)$/) { $cgiparams{'SRC_PORT'} = "1:$2"; } if ($cgiparams{'SRC_PORT'} =~ /^(\d+)\:(\D)$/) { $cgiparams{'SRC_PORT'} = "$1:65535"; } if ($cgiparams{'DEST_PORT'} eq "*") { $cgiparams{'DEST_PORT'} = "1:65535"; } if ($cgiparams{'DEST_PORT'} =~ /^(\D)\:(\d+)$/) { $cgiparams{'DEST_PORT'} = "1:$2"; } if ($cgiparams{'DEST_PORT'} =~ /^(\d+)\:(\D)$/) { $cgiparams{'DEST_PORT'} = "$1:65535"; } # Darren Critchley - Add code for GRE protocol - we want to ignore ports, but we need a place holder if ($cgiparams{'PROTOCOL'} eq 'gre') { $cgiparams{'SRC_PORT'} = "GRE"; $cgiparams{'DEST_PORT'} = "GRE"; } unless($cgiparams{'PROTOCOL'} =~ /^(tcp|udp|gre)$/) { $errormessage = $Lang::tr{'invalid input'}; } # Darren Critchley - Changed how the error routine works a bit - for the validportrange check, we need to # pass in src or dest to determine which side we are working with. # the routine returns the complete error or '' if ($cgiparams{'PROTOCOL'} ne 'gre') { $errormessage = &General::validportrange($cgiparams{'SRC_PORT'}, 'src'); } if( ($cgiparams{'ORIG_IP'} ne "0" && $cgiparams{'KEY2'} ne "0") || $cgiparams{'ACTION'} eq $Lang::tr{'add'}) { # if it is a port forward record with 0 in orig_ip then ignore checking this field unless(&General::validipormask($cgiparams{'ORIG_IP'})) { if ($cgiparams{'ORIG_IP'} ne '') { $errormessage = $Lang::tr{'source ip bad'}; } else { $cgiparams{'ORIG_IP'} = '0.0.0.0/0'; } } } # Darren Critchey - New rule that sets destination same as source if dest_port is blank. if ($cgiparams{'DEST_PORT'} eq ''){ $cgiparams{'DEST_PORT'} = $cgiparams{'SRC_PORT'}; } # Darren Critchey - Just in case error message is already set, this routine would wipe it out if # we don't do a test here if ($cgiparams{'PROTOCOL'} ne 'gre') { unless($errormessage) {$errormessage = &General::validportrange($cgiparams{'DEST_PORT'}, 'dest');} } unless(&General::validip($cgiparams{'DEST_IP'})) { $errormessage = $Lang::tr{'destination ip bad'}; } return; } # Darren Critchley - we want to make sure that a port range does not overlap another port range sub checkportoverlap { my $portrange1 = $_[0]; # New port range my $portrange2 = $_[1]; # existing port range my @tempr1 = split(/\:/,$portrange1); my @tempr2 = split(/\:/,$portrange2); unless (&checkportinc($tempr1[0], $portrange2)){ return 0;} unless (&checkportinc($tempr1[1], $portrange2)){ return 0;} unless (&checkportinc($tempr2[0], $portrange1)){ return 0;} unless (&checkportinc($tempr2[1], $portrange1)){ return 0;} return 1; # Everything checks out! } # Darren Critchley - we want to make sure that a port entry is not within an already existing range sub checkportinc { my $port1 = $_[0]; # Port my $portrange2 = $_[1]; # Port range my @tempr1 = split(/\:/,$portrange2); if ($port1 < $tempr1[0] || $port1 > $tempr1[1]) { return 1; } else { return 0; } } # Darren Critchley - certain ports are reserved for Ipcop # TCP 67,68,81,222,445 # UDP 67,68 # Params passed in -> port, rangeyn, protocol sub disallowreserved { # port 67 and 68 same for tcp and udp, don't bother putting in an array my $msg = ""; my @tcp_reserved = (81,222,444); my $prt = $_[0]; # the port or range my $ryn = $_[1]; # tells us whether or not it is a port range my $prot = $_[2]; # protocol my $srcdst = $_[3]; # source or destination if ($ryn) { # disect port range if ($srcdst eq "src") { $msg = "$Lang::tr{'rsvd src port overlap'}"; } else { $msg = "$Lang::tr{'rsvd dst port overlap'}"; } my @tmprng = split(/\:/,$prt); unless (67 < $tmprng[0] || 67 > $tmprng[1]) { $errormessage="$msg 67"; return; } unless (68 < $tmprng[0] || 68 > $tmprng[1]) { $errormessage="$msg 68"; return; } if ($prot eq "tcp") { foreach my $prange (@tcp_reserved) { unless ($prange < $tmprng[0] || $prange > $tmprng[1]) { $errormessage="$msg $prange"; return; } } } } else { if ($srcdst eq "src") { $msg = "$Lang::tr{'reserved src port'}"; } else { $msg = "$Lang::tr{'reserved dst port'}"; } if ($prt == 67) { $errormessage="$msg 67"; return; } if ($prt == 68) { $errormessage="$msg 68"; return; } if ($prot eq "tcp") { foreach my $prange (@tcp_reserved) { if ($prange == $prt) { $errormessage="$msg $prange"; return; } } } } return; } # Darren Critchley - Attempt to combine Add/Update validation as they are almost the same sub valaddupdate { if ($cgiparams{'KEY2'} eq "0"){ # if it is a port forward rule, then validate properly &validateparams(); } else { # it is an xtaccess rule, just check for a valid ip unless(&General::validipormask($cgiparams{'ORIG_IP'})) { if ($cgiparams{'ORIG_IP'} ne '') { $errormessage = $Lang::tr{'source ip bad'}; } else { # this rule stops someone from adding an ALL xtaccess record $errormessage = $Lang::tr{'xtaccess all error'}; $cgiparams{'ACTION'} = $Lang::tr{'add xtaccess'}; } } # Darren Critchley - check for 0.0.0.0/0 - not allowed for xtaccess if ($cgiparams{'ORIG_IP'} eq "0.0.0.0/0" || $cgiparams{'ORIG_IP'} eq "0.0.0.0") { $errormessage = $Lang::tr{'xtaccess all error'}; $cgiparams{'ACTION'} = $Lang::tr{'add xtaccess'}; } } # Darren Critchley - Remove commas from remarks $cgiparams{'REMARK'} = &Header::cleanhtml($cgiparams{'REMARK'}); # Darren Critchley - Check to see if we are working with port ranges our ($prtrange1, $prtrange2); $_ = $cgiparams{'SRC_PORT'}; if ($cgiparams{'KEY2'} eq "0" && m/:/){ $prtrange1 = 1; } if ($cgiparams{'SRC_IP'} eq '0.0.0.0') { # Dave Roberts - only check if using DEFAULT IP if ($prtrange1 == 1){ # check for source ports reserved for Ipcop &disallowreserved($cgiparams{'SRC_PORT'},1,$cgiparams{'PROTOCOL'},"src"); if ($errormessage) { goto EXITSUB; } } else { # check for source port reserved for Ipcop &disallowreserved($cgiparams{'SRC_PORT'},0,$cgiparams{'PROTOCOL'},"src"); if ($errormessage) { goto EXITSUB; } } } $_ = $cgiparams{'DEST_PORT'}; if ($cgiparams{'KEY2'} eq "0" && m/:/){ $prtrange2 = 1; } if ($cgiparams{'SRC_IP'} eq '0.0.0.0') { # Dave Roberts - only check if using DEFAULT IP if ($prtrange2 == 1){ # check for destination ports reserved for IPFire &disallowreserved($cgiparams{'DEST_PORT'},1,$cgiparams{'PROTOCOL'},"dst"); if ($errormessage) { goto EXITSUB; } } else { # check for destination port reserved for IPFire &disallowreserved($cgiparams{'DEST_PORT'},0,$cgiparams{'PROTOCOL'},"dst"); if ($errormessage) { goto EXITSUB; } } } EXITSUB: return; } # Darren Critchley - Duplicate or overlapping Port range check sub portchecks { $_ = $_[0]; our ($prtrange1, $prtrange2); if (m/:/ && $prtrange1 == 1) { # comparing two port ranges unless (&checkportoverlap($cgiparams{'SRC_PORT'},$_[0])) { $errormessage = "$Lang::tr{'source port overlaps'} $_[0]"; } } if (m/:/ && $prtrange1 == 0 && $errormessage eq '') { # compare one port to a range unless (&checkportinc($cgiparams{'SRC_PORT'}, $_[0])) { $errormessage = "$Lang::tr{'srcprt within existing'} $_[0]"; } } if (! m/:/ && $prtrange1 == 1 && $errormessage eq '') { # compare one port to a range unless (&checkportinc($_[0], $cgiparams{'SRC_PORT'})) { $errormessage = "$Lang::tr{'srcprt range overlaps'} $_[0]"; } } if ($errormessage eq ''){ $_ = $_[1]; if (m/:/ && $prtrange2 == 1) { # if true then there is a port range unless (&checkportoverlap($cgiparams{'DEST_PORT'},$_[1])) { $errormessage = "$Lang::tr{'destination port overlaps'} $_[1]"; } } if (m/:/ && $prtrange2 == 0 && $errormessage eq '') { # compare one port to a range unless (&checkportinc($cgiparams{'DEST_PORT'}, $_[1])) { $errormessage = "$Lang::tr{'dstprt within existing'} $_[1]"; } } if (! m/:/ && $prtrange2 == 1 && $errormessage eq '') { # compare one port to a range unless (&checkportinc($_[1], $cgiparams{'DEST_PORT'})) { $errormessage = "$Lang::tr{'dstprt range overlaps'} $_[1]"; } } } return; }