#!/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; } }