#!/usr/bin/perl # # SmoothWall CGIs # # This code is distributed under the terms of the GPL # # (c) The SmoothWall Team # # Updated: 4/14/2004 - Drew S. Dupont # - Enhanced adding, editing, and deleting of rules # Updated: 4/20/2004 - Drew S. Dupont v0.2 # - Added display of port services # Updated: 4/21/2004 - Drew S. Dupont v0.3 # - Added display of host name resolutuions (if available) # Updated: 5/18/2004 - Soif, www.milliseconde.net # - Merged: Profile (Jo Pitts) + Description 0.3 (Gordon Mckeown) # - Added column sorting # - Added link to toggle service on/off require '/var/smoothwall/header.pl'; use Socket; my (%cgiparams,%selected,%checked); my $filename = "${swroot}/portfw/config"; my $cfg_filename = $filename; my $portfwdir = "${swroot}/portfw"; my $profile_dir = "${swroot}/portfw/profiles"; my $profile_index = "$profile_dir/index"; my $profile_pointer = "$profile_dir/current_profile"; my $desc_length = 16; # Length of description field for each port or range ########################## prepare_profiles(); my $current_profile = get_current_profile(); &showhttpheaders(); $cgiparams{'ENABLED'} = 'off'; $cgiparams{'OLDID'} = 0; &getcgihash(\%cgiparams); my $errormessage = ''; my $updatebutton = 0; my @rules; my @sorted_rules; # grap "SORTBY" or TID values from GET params my @urlvalues = split(/&/, $ENV{'QUERY_STRING'}); foreach my $i (@urlvalues){ ($urlvarname, $urlvardata) = split(/=/, $i); if ($urlvarname eq 'SORTBY'){ $sort_field = $urlvardata; } elsif($urlvarname eq 'TID'){ $toggle_id=$urlvardata; } } $sort_field or $sort_field = 'description'; my $sort_order = 'Ascending'; my $sort_type = 'Alpha'; # or'Alpha'; if($sort_field eq 'src_port' || $sort_field eq 'dest_port'){ $sort_type='Numeric'; } # ---- ADD NEW PROFILE --- if ($cgiparams{'ACTION'} eq 'Add Profile') { my $cpstring = ""; my $pr = $cgiparams{'PNAME'}; chomp $pr; $pr =~ s~[\Q!\$/" '\.*|\E]~_~g; # Replace ! $ / " ' \ . * | characters with _ (underscore) in profile names if ($pr eq '') { $errormessage = 'Profile Name cannot be Empty';} else { if (-e "$profile_dir/$pr") { $errormessage = 'A Profile with that name already exists'; } } unless ($errormessage) { mkdir("$profile_dir/$pr") or die "Failed to create profile directory"; open(FILE, ">> $profile_index") or die "Unable to open index file for output: $!"; flock FILE, 2; print FILE "$pr\n"; close(FILE); system("cp $profile_dir/$current_profile/config.desc $profile_dir/$pr/config.desc"); } } #---- CHANGE CURRENT PROFILE --- if ($cgiparams{'ACTION'} eq 'Use Profile') { my $prname = $cgiparams{'PROFILE'}; chomp $prname; activate_profile($prname); } # ---- REMOVE EXISTING PROFILE --- if ($cgiparams{'ACTION'} eq 'Remove Profile') { my $profile = $cgiparams{'PROFILE'}; chomp($profile); my $rmprofile = "$profile_dir/$profile"; if (-e "$rmprofile/config.desc") { unlink "$rmprofile/config.desc"; } if (-e $rmprofile) { rmdir $rmprofile; } open(FILE, "< $profile_index"); my @profiles = ; close(FILE); open(FILE, "> $profile_index"); foreach my $line (@profiles) { chomp($line); print FILE "$line\n" unless ($line eq $profile); } close(FILE); activate_profile("Default"); } # ---- ADD/UPDATE/Toggle NEW RULE ------------------------------------------------------ if ($cgiparams{'ACTION'} eq $tr{'add'} || $cgiparams{'ACTION'} eq $tr{'update'}){ unless($cgiparams{'PROTOCOL'} =~ /^(tcp|udp)$/) { $errormessage = $tr{'invalid input'}; } unless(&validipormask($cgiparams{'EXT'})){ if ($cgiparams{'EXT'} ne '') { $errormessage = $tr{'source ip bad'}; } else { $cgiparams{'EXT'} = '0.0.0.0/0'; } } unless(&validportrange($cgiparams{'SRC_PORT'})) { $errormessage = $tr{'source port numbers'}; } if ($cgiparams{'DEST_PORT'}) { unless(&validport($cgiparams{'DEST_PORT'})) { $errormessage = $tr{'destination port numbers'}; } } else { $cgiparams{'DEST_PORT'} = 0; } ######################################### # Mod for internal to external forwarding ######################################### unless(&validipormask($cgiparams{'DEST_IP'})){ if ($cgiparams{'DEST_IP'} ne '') { $errormessage = $tr{'destination ip bad'}; } else { $cgiparams{'DEST_IP'} = '0.0.0.0/0'; } } ######################################### open(FILE, "< $profile_dir/$current_profile/config.desc") or die "Unable to open config file for input: $!"; my @current = ; close(FILE); my $line; foreach $line (@current){ my @temp = split(/\,/,$line); if($cgiparams{'SRC_PORT'} eq $temp[1] && $cgiparams{'PROTOCOL'} eq $temp[0]){ $errormessage = "$tr{'source port in use'} $cgiparams{'SRC_PORT'}"; } } unless ($errormessage){ ###################################### # Mod for new edit/update procedure ###################################### if ($cgiparams{'ACTION'} eq $tr{'add'}) { open(FILE, ">> $profile_dir/$current_profile/config.desc") or die "Unable to open config file for output: $!"; flock FILE, 2; print FILE "$cgiparams{'PROTOCOL'},$cgiparams{'EXT'},$cgiparams{'SRC_PORT'},$cgiparams{'DEST_IP'},$cgiparams{'DEST_PORT'},$cgiparams{'ENABLED'},$cgiparams{'DESC'}\n"; close(FILE); &log($tr{'forwarding rule added'}); } else { open(FILE,"> $profile_dir/$current_profile/config.desc") or die "Unable to open config file for output: $!"; flock FILE, 2; $id = 0; foreach $line (@current){ $id++; if ($cgiparams{'OLDID'} eq $id){ print FILE "$cgiparams{'PROTOCOL'},$cgiparams{'EXT'},$cgiparams{'SRC_PORT'},$cgiparams{'DEST_IP'},$cgiparams{'DEST_PORT'},$cgiparams{'ENABLED'},$cgiparams{'DESC'}\n"; } else { print FILE "$line"; } } close(FILE); &log($tr{'forwarding rule updated'}); } ###################################### undef %cgiparams; activate_profile($current_profile); #system('/usr/local/bin/setportfw'); } } # ---- TOGLE RULE ENABLED---------------------------------------------------- if($toggle_id){ open(FILE, "< $profile_dir/$current_profile/config.desc") or die "Unable to open config file for input: $!"; my @current = ; close(FILE); open(FILE,"> $profile_dir/$current_profile/config.desc") or die "Unable to open config file for output: $!"; flock FILE, 2; $id = 0; foreach $line (@current){ $id++; if($toggle_id eq $id){ my @temp = split(/\,/,$line); if($temp[5] eq "on"){$temp[5]='off';}elsif($temp[5] eq "off"){$temp[5]='on';} print FILE "$temp[0],$temp[1],$temp[2],$temp[3],$temp[4],$temp[5],$temp[6]"; } else { print FILE "$line"; } } close(FILE); &log($tr{'forwarding rule updated'}); activate_profile($current_profile); } # ---- EDIT / REMOVE EXISTING RULE ---------------------------------------------------- if ($cgiparams{'ACTION'} eq $tr{'remove'} || $cgiparams{'ACTION'} eq $tr{'edit'}){ open(FILE, "$profile_dir/$current_profile/config.desc") or die "Unable to open config file for input: $!"; my @current = ; close(FILE); my $count = 0; my $id = 0; my $line; foreach $line (@current){ $id++; if ($cgiparams{$id} eq "on") { $count++; } } if ($count == 0) { $errormessage = $tr{'nothing selected'}; } if ($count > 1 && $cgiparams{'ACTION'} eq $tr{'edit'}) { $errormessage = $tr{'you can only select one item to edit'}; } unless ($errormessage){ open(FILE, "> $profile_dir/$current_profile/config.desc") or die "Unable to open config file for output: $!"; flock FILE, 2; $id = 0; foreach $line (@current){ $id++; unless ($cgiparams{$id} eq "on") { print FILE "$line"; } elsif ($cgiparams{'ACTION'} eq $tr{'edit'}) { chomp($line); my @temp = split(/\,/,$line); $cgiparams{'PROTOCOL'} = $temp[0]; $cgiparams{'EXT'} = $temp[1]; $cgiparams{'SRC_PORT'} = $temp[2]; $cgiparams{'DEST_IP'} = $temp[3]; $cgiparams{'DEST_PORT'} = $temp[4]; $cgiparams{'ENABLED'} = $temp[5]; $cgiparams{'DESC'} = $temp[6]; $cgiparams{'OLDID'} = $id; $updatebutton = 1; print FILE "$line\n"; } } close(FILE); if ($cgiparams{'ACTION'} eq $tr{'remove'}){ activate_profile($current_profile); #system('/usr/local/bin/setportfw'); &log($tr{'forwarding rule removed'}); } } } # ---- DISPLAY PAGE ------------------------------------------------- if ($cgiparams{'ACTION'} eq '') { $cgiparams{'PROTOCOL'} = 'tcp'; $cgiparams{'ENABLED'} = 'on'; } $cgiparams{'PNAME'} = ''; my %selected; $selected{'PROTOCOL'}{'udp'} = ''; $selected{'PROTOCOL'}{'tcp'} = ''; $selected{'PROTOCOL'}{$cgiparams{'PROTOCOL'}} = 'SELECTED'; $checked{'ENABLED'}{'off'} = ''; $checked{'ENABLED'}{'on'} = ''; $checked{'ENABLED'}{$cgiparams{'ENABLED'}} = 'CHECKED'; &openpage($tr{'port forwarding configuration'}, 1, '', 'networking'); &shownetworkingsection(); &openbigbox('100%', 'LEFT'); &alertbox($errormessage); print <
Visit smoothwall.co.uk for enhanced commercial SmoothWall products
--> END ; print "
\n"; &openbox('100%', 'LEFT', "Profile Management - Current Profile: $current_profile"); print < New Profile Name: END ; &closebox(); #################################################### # Mod for source ip, destination ip, and destination # port edit display #################################################### if ($cgiparams{'EXT'} eq '0.0.0.0/0') {$sourceip = '';} else {$sourceip = $cgiparams{'EXT'};} if ($cgiparams{'DEST_IP'} eq '0.0.0.0/0') {$destip = '';} else {$destip = $cgiparams{'DEST_IP'};} if ($cgiparams{'DEST_PORT'} eq '0') {$destport = '';} else {$destport = $cgiparams{'DEST_PORT'};} if ($updatebutton) { $buttontext = $tr{'update'}; $boxtext = $tr{'update current rule'}; } else { $buttontext = $tr{'add'}; $boxtext = $tr{'add a new rule'}; } ############################################## &openbox('100%', 'LEFT', $boxtext); print < $tr{'sourcec'} $tr{'source port or rangec'} $tr{'destinationc'} $tr{'destination portc'} 
$tr{'descriptionc'} $tr{'enabled'}

  $tr{'portfw destination port'} END ; &closebox(); # show current rules #################################################### &openbox('100%', 'LEFT', $tr{'current rules'}); my $sortlink="?SORTBY"; print < $tr{'description'} $tr{'protocol'} $tr{'source ip'} $tr{'source port'} $tr{'destination ip'} $tr{'destination port'} $tr{'enabledtitle'} $tr{'mark'} END ; preload_config(); # Loads the config information into an array of hashes my $id = 0; foreach $field (@sorted_rules) { $id++; my $checked = ''; my $srcportname = ''; my $destportname = ''; my $remotehostname = ''; my $localhostname = ''; my $f_protocol = $$field{'f_protocol'}; # $field[0]; my $f_src_ip = $$field{'f_src_ip'}; # $field[1]; my $f_src_port = $$field{'f_src_port'}; # $field[2]; my $f_dest_ip = $$field{'f_dest_ip'}; # $field[3]; my $f_dest_port = $$field{'f_dest_port'}; # $field[4]; my $f_description = $$field{'f_description'}; my $f_enabled = $$field{'f_enabled'}; #''; my $line_id = $$field{'line_id'}; my $link_enabled = "?TID=$line_id&SORTBY=$sort_field"; if ($cgiparams{'OLDID'} eq $line_id) { $checked = 'CHECKED'; } if ($id % 2) { print "\n"; } else { print "\n"; } print <$f_description $f_protocol $f_src_ip $f_src_port $f_dest_ip $f_dest_port END ; } print <
END ; &closebox(); &alertbox('add','add'); &closebigbox(); &closepage(); # ######### Functions ################################################################################## sub prepare_profiles { mkdir $profile_dir if (! -e $profile_dir); # Create the profiles folder if it doesn't yet exist if (! -e "$profile_dir/Default") { mkdir "$profile_dir/Default"; # Create a Default profile pfw_convert("$portfwdir/config","$profile_dir/Default/config.desc"); } if (! -e $profile_pointer) { open OUTFILE, "> $profile_pointer" or die "Could not open current_profile for output: $!"; print OUTFILE "Default"; close(OUTFILE); } if (! -e $profile_index) { open OUTFILE, "> $profile_index" or die "Could not open profile index for output: $!"; print OUTFILE "Default\n"; close(OUTFILE); } } sub pfw_convert { my $source_file = shift; my $dest_file = shift; open INFILE, "< $source_file" or die "Could not open port fw config file for input: $!"; open OUTFILE, "> $dest_file" or die "Could not open port fw config descriptions file for output: $!"; flock INFILE, 2; flock OUTFILE, 2; while() { chomp; print OUTFILE "$_,\n"; # Add a comma to the end of each line } close(INFILE); close(OUTFILE); } sub activate_profile { my $profile_name = shift; open INFILE, "< $profile_dir/$profile_name/config.desc" or die "Could not open port fw config descriptions file for input: $!"; open OUTFILE, "> $cfg_filename" or die "Could not open port fw config file for output: $!"; while() { chomp; my @entries = split(/\,/); print OUTFILE join(",", @entries[0, 1, 2, 3, 4, 5]) . "\n"; # Cut off the description field } close(INFILE); close(OUTFILE); open(OUTFILE, "> $profile_pointer"); # Set the current profile flock OUTFILE, 2; print OUTFILE $profile_name; close(OUTFILE); &log('Profile Changed'); system("/usr/local/bin/setportfw"); $current_profile = $profile_name; } sub get_current_profile { my $current_prof; open(CURRPROFILE, "$profile_dir/current_profile"); while () { my @temp = split(/\,/); $current_prof = $temp[0]; } close(CURRPROFILE); $current_prof = "Default" if ($current_prof eq ''); return $current_prof; } sub sort_ascend { return ($$a{$sort_field} cmp $$b{$sort_field}) if ($sort_type eq 'Alpha'); return ($$a{$sort_field} <=> $$b{$sort_field}) if ($sort_type eq 'Numeric'); return 0; } sub sort_descend { return ($$b{$sort_field} cmp $$a{$sort_field}) if ($sort_type eq 'Alpha'); return ($$b{$sort_field} <=> $$a{$sort_field}) if ($sort_type eq 'Numeric'); return 0; } sub preload_config { my $i = 0; open(RULES, "< $profile_dir/$current_profile/config.desc") or die "Unable to open config file for input: $!"; while (){ chomp; my @temp = split(/\,/); $rules[$i]{'protocol'} = $temp[0]; $rules[$i]{'f_protocol'} = uc($temp[0]); $rules[$i]{'src_ip'} = $temp[1]; $rules[$i]{'f_src_ip'} = $temp[1]; if ($temp[1] eq '0.0.0.0/0'){ $rules[$i]{'f_src_ip'} = $tr{'any'}; } else { if ($src_hostname = gethostbyaddr(inet_aton($temp[1]), AF_INET)) { $rules[$i]{'f_src_ip'} = "$temp[1] $src_hostname"; } } $rules[$i]{'dest_ip'} = $temp[3]; $rules[$i]{'f_dest_ip'} = $temp[3]; if ($temp[3] eq '0.0.0.0/0'){ $rules[$i]{'f_dest_ip'} = $tr{'any'}; } else { if ($dest_hostname = gethostbyaddr(inet_aton($temp[3]), AF_INET)) { $rules[$i]{'f_dest_ip'} = "$temp[3] $dest_hostname"; } } $rules[$i]{'src_port'} = $temp[2]; $rules[$i]{'f_src_port'} = $temp[2]." ".getservbyport($temp[2],$temp[0]); $rules[$i]{'f_src_port'} =~ s/(\t)/ /g; # Get rid of 'tabs' in service name. $rules[$i]{'dest_port'} = $temp[4]; if (($temp[4] eq '0') || ($temp[4] eq $temp[2])){ $rules[$i]{'f_dest_port'} = $rules[$i]{'f_src_port'}; ##'N/A' } else { $rules[$i]{'f_dest_port'} = $rules[$i]{'f_src_port'}."
".$temp[4]." ".getservbyport($temp[4],$temp[0]); $rules[$i]{'f_dest_port'} =~ s/(\t)/ /g; # Get rid of 'tabs' in service name. } $rules[$i]{'enabled'} = $temp[5]; if ($temp[5] eq 'on') { $rules[$i]{'f_enabled'} = '/ui/assets/3.5/img/on.gif'; $rules[$i]{'disable'} = 'off'; } else { $rules[$i]{'f_enabled'} = '/ui/assets/3.5/img/off.gif'; $rules[$i]{'disable'} = 'on'; } $rules[$i]{'description'} = $temp[6]; $rules[$i]{'f_description'} = substr($temp[6], 0, $desc_length); $rules[$i]{'line_id'} = $i+1; $i++; } close(RULES); if ($sort_order eq "Descending") { @sorted_rules = sort sort_descend @rules; } else { @sorted_rules = sort sort_ascend @rules; } }