#!/usr/bin/perl
###############################################################################
# #
# IPFire.org - A linux based firewall #
# Copyright (C) 2007 Michael Tremer & Christian Schmidt #
# #
# 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 3 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, see . #
# #
###############################################################################
use strict;
require '/var/ipfire/general-functions.pl';
require "${General::swroot}/lang.pl";
require "${General::swroot}/header.pl";
my @icmptypes = &get_icmptypes();
&Header::showhttpheaders();
my %cgiparams=();
my %selected=();
my %checked=();
my $filename = "${General::swroot}/firewall/customservices";
my $key = 0; # used for finding last sequence number used
# Darren Critchley - vars for setting up sort order
my $sort_col = '1';
my $sort_type = 'a';
my $sort_dir = 'asc';
if ($ENV{'QUERY_STRING'} ne '') {
my ($item1, $item2, $item3) = split(/\&/,$ENV{'QUERY_STRING'});
if ($item1 ne '') {
($junk, $sort_col) = split(/\=/,$item1)
}
if ($item2 ne '') {
($junk, $sort_type) = split(/\=/,$item2)
}
if ($item3 ne '') {
($junk, $sort_dir) = split(/\=/,$item3)
}
}
$cgiparams{'KEY'} = '';
$cgiparams{'PORTS'} = '';
$cgiparams{'PROTOCOL'} = '6';
$cgiparams{'NAME'} = '';
$cgiparams{'PORT_INVERT'} = 'off';
$cgiparams{'PROTOCOL_INVERT'} = 'off';
$cgiparams{'ICMP'} = 'BLANK';
&Header::getcgihash(\%cgiparams);
if ($cgiparams{'ACTION'} eq $Lang::tr{'add'}){
&validateparams();
unless($errormessage){
$key++; # Add one to last sequence number
open(FILE,">>$filename") or die 'Unable to open config file.';
flock FILE, 2;
print FILE "$key,$cgiparams{'NAME'},$cgiparams{'PORTS'},$cgiparams{'PROTOCOL'},$cgiparams{'PORT_INVERT'},$cgiparams{'PROTOCOL_INVERT'},$cgiparams{'ICMP'}\n";
close(FILE);
&General::log("$Lang::tr{'service added'}: $cgiparams{'NAME'}");
undef %cgiparams;
}
}
if ($cgiparams{'ACTION'} eq $Lang::tr{'update'})
{
&validateparams();
# Darren Critchley - If there is an error don't waste any more processing time
if ($errormessage) { $cgiparams{'ACTION'} = $Lang::tr{'edit'}; goto UPD_ERROR; }
unless($errormessage){
open(FILE, $filename) or die 'Unable to open custom services file.';
my @current = ;
close(FILE);
my $line;
open(FILE, ">$filename") or die 'Unable to open config file.';
flock FILE, 2;
foreach $line (@current) {
chomp($line);
my @temp = split(/\,/,$line);
if ($cgiparams{'KEY'} eq $temp[0]) {
print FILE "$cgiparams{'KEY'},$cgiparams{'NAME'},$cgiparams{'PORTS'},$cgiparams{'PROTOCOL'},$cgiparams{'PORT_INVERT'},$cgiparams{'PROTOCOL_INVERT'},$cgiparams{'ICMP'}\n";
} else {
print FILE "$line\n";
}
}
close(FILE);
&General::log("$Lang::tr{'service updated'}: $cgiparams{'NAME'}");
undef %cgiparams;
}
UPD_ERROR:
}
if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'})
{
open(FILE, "$filename") or die 'Unable to open custom services file.';
my @current = ;
close(FILE);
unless ($errormessage)
{
foreach my $line (@current)
{
chomp($line);
my @temp = split(/\,/,$line);
if ($cgiparams{'KEY'} eq $temp[0]) {
$cgiparams{'NAME'} = $temp[1];
$cgiparams{'PORTS'} = $temp[2];
$cgiparams{'PROTOCOL'} = $temp[3];
$cgiparams{'PORT_INVERT'} = $temp[4];
$cgiparams{'PROTOCOL_INVERT'} = $temp[5];
$cgiparams{'ICMP'} = $temp[6];
}
}
}
}
if ($cgiparams{'ACTION'} eq $Lang::tr{'remove'})
{
open(FILE, $filename) or die 'Unable to open custom services file.';
my @current = ;
close(FILE);
open(FILE, ">$filename") or die 'Unable to open custom services file.';
flock FILE, 2;
foreach my $line (@current)
{
chomp($line);
if ($line ne '') {
my @temp = split(/\,/,$line);
if ($cgiparams{'KEY'} eq $temp[0]) {
&General::log("$Lang::tr{'service removed'}: $temp[1]");
} else {
print FILE "$temp[0],$temp[1],$temp[2],$temp[3],$temp[4],$temp[5],$temp[6]\n";
}
}
}
close(FILE);
undef %cgiparams;
}
if ($cgiparams{'ACTION'} eq $Lang::tr{'reset'})
{
undef %cgiparams;
}
if ($cgiparams{'ACTION'} eq '')
{
$cgiparams{'KEY'} = '';
$cgiparams{'PORTS'} = '';
$cgiparams{'PROTOCOL'} = '6';
$cgiparams{'NAME'} = '';
$cgiparams{'PORT_INVERT'} = 'off';
$cgiparams{'PROTOCOL_INVERT'} = 'off';
$cgiparams{'ICMP'} = 'BLANK';
}
# Darren Critchley - Bring in the protocols file built from /etc/protocols into hash %protocol
require "${General::swroot}/firewall/protocols.pl";
# Darren Critchley - figure out which protocol is selected
$selected{'PROTOCOL'}{'tcpudp'}= '';
$selected{'PROTOCOL'}{'all'}= '';
foreach $line (keys %protocols) {
# $selected{'PROTOCOL'}{"$protocols{$line}"}= '';
$selected{'PROTOCOL'}{$line}= '';
}
$selected{'PROTOCOL'}{$cgiparams{'PROTOCOL'}} = 'SELECTED';
# Darren Critchley - figure out which icmptype is selected
$selected{'ICMP'}{$cgiparams{'ICMP'}} = 'SELECTED';
$checked{'PORT_INVERT'}{'off'} = '';
$checked{'PORT_INVERT'}{'on'} = '';
$checked{'PORT_INVERT'}{$cgiparams{'PORT_INVERT'}} = 'CHECKED';
$checked{'PROTOCOL_INVERT'}{'off'} = '';
$checked{'PROTOCOL_INVERT'}{'on'} = '';
$checked{'PROTOCOL_INVERT'}{$cgiparams{'PROTOCOL_INVERT'}} = 'CHECKED';
&Header::openpage($Lang::tr{'services settings'}, 1, '');
&Header::openbigbox('100%', 'LEFT', '', $errormessage);
# DEBUG DEBUG
#&Header::openbox('100%', 'LEFT', 'DEBUG');
#foreach $line (keys %cgiparams) {
# print "$line = $cgiparams{$line} ";
#}
#print "$sort_col\n";
#print "$ENV{'QUERY_STRING'}\n";
#print " \n";
#&Header::closebox();
if ($errormessage) {
&Header::openbox('100%', 'LEFT', $Lang::tr{'error messages'});
print "$errormessage\n";
print " \n";
&Header::closebox();
}
if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'}){
&Header::openbox('100%', 'LEFT', "$Lang::tr{'edit service'}:");
} else {
&Header::openbox('100%', 'LEFT', "$Lang::tr{'add service'}:");
}
# Darren Critchley - Show protocols with TCP, UDP, etc at the top of the list.
print <
$Lang::tr{'servicename'}
$Lang::tr{'invert'}
$Lang::tr{'ports'}
$Lang::tr{'invert'}
$Lang::tr{'protocol'}
$Lang::tr{'icmp type'}:
END
;
if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'}){
print "
\n";
print "\n";
print "
\n";
} else {
print "
\n";
print "
\n";
}
print <
END
;
&Header::closebox();
&Header::openbox('100%', 'LEFT', "$Lang::tr{'custom services'}:");
print <
END
;
if ($sort_dir eq 'asc' && $sort_col eq '2') {
print "
END
;
&display_custom_services();
print <
END
;
&Header::closebox();
&Header::openbox('100%', 'LEFT', "$Lang::tr{'default services'}:");
print <
$Lang::tr{'servicename'}
$Lang::tr{'ports'}
$Lang::tr{'protocol'}
END
;
&display_default_services();
print <
END
;
&Header::closebox();
print "$Lang::tr{'this feature has been sponsored by'} : ";
print "Kobelt Development Inc..\n";
&Header::closebigbox();
&Header::closepage();
sub display_custom_services
{
open(FILE, "$filename") or die 'Unable to open services file.';
my @current = ;
close(FILE);
my $id = 0;
my $port_inv = '';
my $prot_inv = '';
my $port_inv_tail = '';
my $prot_inv_tail = '';
my @outarray = &General::srtarray($sort_col,$sort_type,$sort_dir,@current);
foreach $line (@outarray)
{
chomp($line);
if ($line ne ''){
my @temp = split(/\,/,$line);
# Darren Critchley highlight the row we are editing
if ( $cgiparams{'ACTION'} eq $Lang::tr{'edit'} && $cgiparams{'KEY'} eq $temp[0] ) {
print "
\n";
$id++;
}
}
}
sub display_default_services
{
my $fname = "${General::swroot}/firewall/defaultservices";
my $prev = "";
my $newline="";
open(FILE, "$fname") or die 'Unable to open default services file.';
my @current = ;
close(FILE);
my $id = 0;
foreach my $line (sort @current)
{
my @temp = split(/\,/,$line);
if ($id % 2) {
print "
\n";
} else {
print "
\n";
}
print "
$temp[0]
\n";
print "
$temp[1]
\n";
print "
" . &cleanprotocol("$temp[2]") . "
\n";
print "
\n";
$id++;
}
}
sub cleanprotocol
{
my $prtcl = $_[0];
chomp($prtcl);
if ($prtcl eq 'tcpudp') {
$prtcl = 'TCP & UDP';
} else {
$prtcl = uc($prtcl);
}
return $prtcl;
}
sub cleanport
{
my $prt = $_[0];
chomp($prt);
# Darren Critchley - Format the ports
$prt =~ s/-/ - /;
$prt =~ s/:/ - /;
return $prt;
}
# Validate Field Entries
sub validateparams
{
$erromessage='';
if ($cgiparams{'PROTOCOL'} eq 'tcp' || $cgiparams{'PROTOCOL'} eq 'udp' || $cgiparams{'PROTOCOL'} eq 'tcpudp' || $cgiparams{'PROTOCOL'} eq 'all') {
# Darren Critchley - Get rid of dashes in port ranges
$cgiparams{'PORTS'}=~ tr/-/:/;
# Darren Critchley - code to substitue wildcards
if ($cgiparams{'PORTS'} eq "*") {
$cgiparams{'PORTS'} = "1:65535";
}
if ($cgiparams{'PORTS'} =~ /^(\D)\:(\d+)$/) {
$cgiparams{'PORTS'} = "1:$2";
}
if ($cgiparams{'PORTS'} =~ /^(\d+)\:(\D)$/) {
$cgiparams{'PORTS'} = "$1:65535";
}
# Darren Critchley - watch the order here, the validportrange sets errormessage=''
$errormessage = &General::validportrange($cgiparams{'PORTS'}, 'src');
if ($errormessage) {return;}
} else {
$cgiparams{'PORTS'} = "";
}
if ($cgiparams{'PROTOCOL'} eq 'tcp') {
$cgiparams{'ICMP'} = "BLANK";
}
if($cgiparams{'PORTS'} eq '' && $cgiparams{'PORT_INVERT'} ne 'off'){
$cgiparams{'PORT_INVERT'} = 'off';
}
if ($cgiparams{'NAME'} eq '') {
$errormessage = $Lang::tr{'noservicename'};
return;
}
if ($cgiparams{'PROTOCOL'} eq 'icmp' && $cgiparams{'ICMP'} eq 'BLANK'){
$errormessage = $Lang::tr{'icmp selected but no type'};
return;
}
unless($errormessage){
$cgiparams{'NAME'}=&Header::cleanhtml($cgiparams{'NAME'});
open(FILE, $filename) or die 'Unable to open custom services file.';
my @current = ;
close(FILE);
foreach my $line (@current)
{
chomp($line);
if ($line ne '') {
my @temp = split(/\,/,$line);
if ($cgiparams{'NAME'} eq $temp[1] && $cgiparams{'KEY'} ne $temp[0]) {
$errormessage=$Lang::tr{'duplicate name'};
return;
}
$key=$temp[0];
}
}
unless($errormessage){
my $fname = "${General::swroot}/firewall/defaultservices";
my $prev = "";
my $newline="";
open(FILE, "$fname") or die 'Unable to open default services file.';
my @current = ;
close(FILE);
foreach my $line (sort @current)
{
my @temp = split(/\,/,$line);
if ($cgiparams{'NAME'} eq $temp[0]) {
$errormessage=$Lang::tr{'duplicate name'};
return;
}
}
}
}
}
sub get_icmptypes
{
my $fname = "${General::swroot}/firewall/icmptypes";
my $newline="";
my @newarray=();
open(FILE, "$fname") or die 'Unable to open icmp file.';
my @current = ;
close(FILE);
foreach $newline (sort @current)
{
chomp ($newline);
if (substr($newline, 0, 1) ne "#") {
push (@newarray, $newline);
}
}
return (@newarray);
}