mirror of
https://github.com/webmin/webmin.git
synced 2025-07-23 00:30:33 +00:00
341 lines
8.2 KiB
Perl
Executable File
341 lines
8.2 KiB
Perl
Executable File
#!/usr/local/bin/perl
|
|
# Create, update or delete a firewall rule
|
|
|
|
require './ipfw-lib.pl';
|
|
&ReadParse();
|
|
&error_setup($text{'save_err'});
|
|
|
|
$rules = &get_config();
|
|
if ($in{'new'}) {
|
|
# Find the last editable rule
|
|
if ($rules->[@$rules-1]->{'num'} == 65535 &&
|
|
@$rules > 1) {
|
|
$lastidx = $rules->[@$rules-2]->{'index'};
|
|
}
|
|
else {
|
|
$lastidx = $rules->[@$rules-1]->{'index'};
|
|
}
|
|
|
|
# Work out where to insert, and what number to use
|
|
if ($in{'before'} ne '') {
|
|
# Adding before some rule
|
|
local $pn = $in{'before'} == 0 ? 0 :
|
|
$rules->[$in{'before'}-1]->{'num'};
|
|
$rule = { 'num' => ($rules->[$in{'before'}]->{'num'}+$pn)/2 };
|
|
splice(@$rules, $in{'before'}, 0, $rule);
|
|
}
|
|
elsif ($in{'after'} ne '') {
|
|
# Adding after some rule
|
|
local $nn = $in{'after'} == $lastidx ?
|
|
$rules->[$in{'after'}]->{'num'}+200 :
|
|
$rules->[$in{'after'}+1]->{'num'};
|
|
$rule = { 'num' => ($rules->[$in{'after'}]->{'num'}+$nn)/2 };
|
|
splice(@$rules, $in{'after'}+1, 0, $rule);
|
|
}
|
|
elsif (!$in{'num_def'}) {
|
|
# At specified number
|
|
$in{'num'} =~ /^\d+$/ && $in{'num'} >= 0 && $in{'num'} < 65536
|
|
|| &error($text{'save_enum'});
|
|
$rule = { 'num' => $in{'num'} };
|
|
my $found = 0;
|
|
for(my $i=0; $i<@$rules; $i++) {
|
|
if ($rules->[$i]->{'num'} >= $in{'num'}) {
|
|
splice(@$rules, $i, 0, $rule);
|
|
$found++;
|
|
last;
|
|
}
|
|
}
|
|
push(@$rules, $rule) if (!$found);
|
|
}
|
|
elsif (!@$rules) {
|
|
# First rule
|
|
$rule = { 'num' => '00100' };
|
|
push(@$rules, $rule);
|
|
}
|
|
else {
|
|
# At end or before last deny-all rule
|
|
$rule = { 'num' => $rules->[$lastidx]->{'num'}+100 };
|
|
splice(@$rules, $lastidx+1, 0, $rule);
|
|
}
|
|
$rule->{'num'} = sprintf "%5.5d", $rule->{'num'};
|
|
}
|
|
else {
|
|
$rule = $rules->[$in{'idx'}];
|
|
delete($rule->{'text'});
|
|
}
|
|
|
|
if ($in{'delete'}) {
|
|
# Just remove this rule
|
|
splice(@$rules, $in{'idx'}, 1);
|
|
}
|
|
else {
|
|
# Validate inputs and contruct the rule object
|
|
$in{'cmt'} =~ s/\r//g;
|
|
$rule->{'cmt'} = $in{'cmt'};
|
|
|
|
# Parse rule action and arg
|
|
$rule->{'action'} = $in{'action'};
|
|
if ($in{'action'} eq "skipto") {
|
|
$in{'action_skipto'} =~ /^\d+$/ ||
|
|
&error($text{'save_eskipto'});
|
|
$rule->{'aarg'} = $in{'action_skipto'};
|
|
}
|
|
elsif ($in{'action'} eq "fwd") {
|
|
&check_ipaddress($in{'action_fwdip'}) ||
|
|
&error($text{'save_efwdip'});
|
|
if ($in{'action_fwdport'} eq "") {
|
|
$rule->{'aarg'} = $in{'action_fwdip'};
|
|
}
|
|
else {
|
|
$in{'action_fwdport'} =~ /^\d+$/ ||
|
|
&error($text{'save_efwdport'});
|
|
$rule->{'aarg'} = $in{'action_fwdip'}.",".
|
|
$in{'action_fwdport'};
|
|
}
|
|
}
|
|
elsif ($in{'action'} eq "divert" || $in{'action'} eq "pipe" ||
|
|
$in{'action'} eq "queue" || $in{'action'} eq "tee") {
|
|
$in{'action_port'} =~ /^\d+$/ ||
|
|
&error($text{'save_eteeport'});
|
|
$rule->{'aarg'} = $in{'action_port'};
|
|
}
|
|
elsif ($in{'action'} eq "unreach") {
|
|
$rule->{'aarg'} = $in{'action_unreach'};
|
|
}
|
|
else {
|
|
delete($rule->{'aarg'});
|
|
}
|
|
|
|
# Parse protocol
|
|
if ($in{'proto_orblock'}) {
|
|
$rule->{'proto'} = &parse_orblock("proto");
|
|
}
|
|
else {
|
|
$rule->{'proto'} = $in{'proto'};
|
|
}
|
|
|
|
# Parse in/out option
|
|
delete($rule->{'in'});
|
|
delete($rule->{'out'});
|
|
delete($rule->{'in_not'});
|
|
delete($rule->{'out_not'});
|
|
if ($in{'inout'} == 1) {
|
|
$rule->{'in'} = 1;
|
|
}
|
|
elsif ($in{'inout'} == 2) {
|
|
$rule->{'out'} = 1;
|
|
}
|
|
|
|
# Parse via interface
|
|
$rule->{'via'} = &parse_interface("via");
|
|
|
|
# Parse logging level
|
|
if ($in{'log'}) {
|
|
$rule->{'log'} = 1;
|
|
if ($in{'logamount'} ne "") {
|
|
$in{'logamount'} =~ /^\d+$/ ||
|
|
&error($text{'save_elogamount'});
|
|
$rule->{'logamount'} = $in{'logamount'};
|
|
}
|
|
else {
|
|
delete($rule->{'logamount'});
|
|
}
|
|
}
|
|
else {
|
|
$rule->{'log'} = 0;
|
|
}
|
|
|
|
# Parse source and destination
|
|
foreach $s ("from", "to") {
|
|
# IP address
|
|
if ($in{$s."_orblock"}) {
|
|
$rule->{$s} = &parse_orblock($s);
|
|
}
|
|
elsif ($in{$s."_mode"} == 0) {
|
|
$rule->{$s} = "any";
|
|
}
|
|
elsif ($in{$s."_mode"} == 1) {
|
|
$rule->{$s} = "me";
|
|
}
|
|
else {
|
|
&to_ipaddress($in{$s}) ||
|
|
($in{$s} =~ /^([0-9\.]+)\/(\d+)$/ &&
|
|
&check_ipaddress("$1")) ||
|
|
($in{$s} =~ /^([0-9\.]+)\/(\d+)\{([0-9,]+)\}$/ &&
|
|
&check_ipaddress("$1") &&
|
|
$ipfw_version >= 2) ||
|
|
&error($text{'save_e'.$s});
|
|
$rule->{$s} = $in{$s};
|
|
}
|
|
|
|
# Port numbers
|
|
if ($in{$s."_ports_orblock"}) {
|
|
# XXX could be optional?
|
|
$rule->{$s."_ports"} = &parse_orblock($s."_ports");
|
|
}
|
|
elsif ($in{$s."_ports_mode"} == 0) {
|
|
delete($rule->{$s."_ports"});
|
|
}
|
|
else {
|
|
local $p = $rule->{'proto'};
|
|
$p eq "tcp" || $p eq "udp" || $p eq "ip" ||
|
|
$ipfw_version >= 2 ||
|
|
&error($text{'save_eportsproto'.$s});
|
|
$in{$s."_ports"} =~ /^\d+$/ ||
|
|
getservbyname($in{$s."_ports"}, $p) ||
|
|
$in{$s."_ports"} =~ /^\d+\-\d+$/ ||
|
|
($in{$s."_ports"} =~ /^([a-z0-9]+)\-([a-z0-9]+)$/i &&
|
|
getservbyname($1, $p) && getservbyname($2, $p)) ||
|
|
$in{$s."_ports"} =~ /^([a-z0-9]+)(,[a-z0-9]+)*$/ ||
|
|
($in{$s."_ports"} =~ /^([a-z0-9]+|([a-z0-9]+)\-([a-z0-9]+))(,[a-z0-9]+|,([a-z0-9]+)\-([a-z0-9]+))*$/ &&
|
|
$ipfw_version >= 2) ||
|
|
&error($text{'save_eports'.$s});
|
|
$rule->{$s."_ports"} = $in{$s."_ports"};
|
|
$rule->{$s."_ports_not"} = $in{$s."_ports_not"}
|
|
if ($ipfw_version >= 2);
|
|
}
|
|
}
|
|
$rule->{'xmit'} = &parse_interface("xmit");
|
|
$rule->{'recv'} = &parse_interface("recv");
|
|
|
|
# XXX multiple options
|
|
|
|
# Parse various options
|
|
&parse_yes_no_ignored("established");
|
|
&parse_yes_no_ignored("keep-state");
|
|
&parse_yes_no_ignored("bridged");
|
|
&parse_yes_no_ignored("frag");
|
|
&parse_yes_no_ignored("setup");
|
|
|
|
# Parse MAC address
|
|
if ($ipfw_version >= 2) {
|
|
if ($in{'mac1_def'} && $in{'mac2_def'}) {
|
|
delete($rule->{'mac'});
|
|
}
|
|
else {
|
|
local @mac;
|
|
if ($in{'mac2_def'}) {
|
|
push(@mac, "any");
|
|
}
|
|
else {
|
|
$in{'mac2'} =~ /^[0-9a-f]{2}(:[0-9a-f]{2}){5}(\/\d+)?$/ || &error($text{'save_emac2'});
|
|
push(@mac, $in{'mac2'});
|
|
}
|
|
if ($in{'mac1_def'}) {
|
|
push(@mac, "any");
|
|
}
|
|
else {
|
|
$in{'mac1'} =~ /^[0-9a-f]{2}(:[0-9a-f]{2}){5}(\/\d+)?$/ || &error($text{'save_emac1'});
|
|
push(@mac, $in{'mac1'});
|
|
}
|
|
$rule->{'mac'} = \@mac;
|
|
}
|
|
}
|
|
|
|
# Parse UID and GID
|
|
if ($in{'uid_def'}) {
|
|
delete($rule->{'uid'});
|
|
}
|
|
elsif ($in{'uid'} =~ /^#(\d+)$/) {
|
|
$rule->{'uid'} = $1;
|
|
}
|
|
else {
|
|
defined($rule->{'uid'} = getpwnam($in{'uid'})) ||
|
|
&error($text{'save_euid'});
|
|
}
|
|
if ($in{'gid_def'}) {
|
|
delete($rule->{'gid'});
|
|
}
|
|
elsif ($in{'gid'} =~ /^#(\d+)$/) {
|
|
$rule->{'gid'} = $1;
|
|
}
|
|
else {
|
|
defined($rule->{'gid'} = getgrnam($in{'gid'})) ||
|
|
&error($text{'save_egid'});
|
|
}
|
|
|
|
# Parse ICMP types
|
|
if ($in{'icmptypes'}) {
|
|
$rule->{'proto'} eq 'icmp' || &error($text{'save_eicmptypes'});
|
|
$rule->{'icmptypes'} = join(",", split(/\0/, $in{'icmptypes'}));
|
|
}
|
|
else {
|
|
delete($rule->{'icmptypes'});
|
|
}
|
|
|
|
# Parse tcp flags
|
|
if ($in{'tcpflags'}) {
|
|
$rule->{'proto'} eq 'tcp' || &error($text{'save_etcpflags'});
|
|
$rule->{'tcpflags'} = join(",", split(/\0/, $in{'tcpflags'}));
|
|
}
|
|
else {
|
|
delete($rule->{'tcpflags'});
|
|
}
|
|
|
|
# Parse limit directive
|
|
if ($in{'limit'}) {
|
|
$in{'limit2'} =~ /^\d+$/ || &error($text{'save_elimit'});
|
|
$rule->{'limit'} = [ $in{'limit'}, $in{'limit2'} ];
|
|
}
|
|
else {
|
|
delete($rule->{'limit'});
|
|
}
|
|
|
|
# Parse dst-port and src-port directive
|
|
foreach $ds ('dst', 'src') {
|
|
if (!$in{$ds.'port_def'}) {
|
|
local @dstports = split(/[ ,]+/, $in{$ds.'port'});
|
|
foreach $p (@dstports) {
|
|
&valid_port($p, $rule->{'proto'}) ||
|
|
&error($text{'save_e'.$ds.'port'});
|
|
}
|
|
$rule->{$ds.'-port'} = \@dstports;
|
|
}
|
|
else {
|
|
delete($rule->{$ds.'-port'});
|
|
}
|
|
}
|
|
}
|
|
|
|
# Save all rules
|
|
&lock_file($ipfw_file);
|
|
&save_config($rules);
|
|
&unlock_file($ipfw_file);
|
|
©_to_cluster();
|
|
&webmin_log($in{'delete'} ? "delete" : $in{'new'} ? "create" : "modify",
|
|
"rule", $rule->{'action'}, $rule);
|
|
&redirect("");
|
|
|
|
# parse_interface(name)
|
|
sub parse_interface
|
|
{
|
|
local $iface = $in{$_[0]} eq "other" ? $in{$_[0]."_other"} : $in{$_[0]};
|
|
return undef if (!$iface);
|
|
$iface =~ /^\S+$/ || &error($text{'save_e'.$_[0]});
|
|
return $iface;
|
|
}
|
|
|
|
# parse_orblock(name)
|
|
sub parse_orblock
|
|
{
|
|
$in{$_[0]} =~ /\S/ || &error(&text('save_eorblock'.$_[0]));
|
|
return [ split(/\s+/, $in{$_[0]}) ];
|
|
}
|
|
|
|
# parse_yes_no_ignored(name)
|
|
sub parse_yes_no_ignored
|
|
{
|
|
if ($in{$_[0]} == 0) {
|
|
delete($rule->{$_[0]});
|
|
}
|
|
elsif ($in{$_[0]} == 1) {
|
|
$rule->{$_[0]} = 1;
|
|
$rule->{$_[0]."_not"} = 0;
|
|
}
|
|
elsif ($in{$_[0]} == 2) {
|
|
$rule->{$_[0]} = 1;
|
|
$rule->{$_[0]."_not"} = 1;
|
|
}
|
|
}
|
|
|