mirror of
https://github.com/vincentmli/bpfire.git
synced 2026-04-11 11:35:54 +02:00
If the firewall is part of a local network (e.g. GREEN), we automatically add rules that grant/forbid access for the firewall, too. This has been broken for various default policies other than ALLOWED.
537 lines
13 KiB
Perl
Executable File
537 lines
13 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
###############################################################################
|
|
# #
|
|
# IPFire.org - A linux based firewall #
|
|
# Copyright (C) 2013 Alexander Marx <amarx@ipfire.org> #
|
|
# #
|
|
# 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 <http://www.gnu.org/licenses/>. #
|
|
# #
|
|
###############################################################################
|
|
|
|
use strict;
|
|
no warnings 'uninitialized';
|
|
|
|
package fwlib;
|
|
|
|
my %customnetwork=();
|
|
my %customhost=();
|
|
my %customgrp=();
|
|
my %customservice=();
|
|
my %customservicegrp=();
|
|
my %ccdnet=();
|
|
my %ccdhost=();
|
|
my %ipsecconf=();
|
|
my %ipsecsettings=();
|
|
my %netsettings=();
|
|
my %ovpnsettings=();
|
|
my %aliases=();
|
|
|
|
require '/var/ipfire/general-functions.pl';
|
|
|
|
my $confignet = "${General::swroot}/fwhosts/customnetworks";
|
|
my $confighost = "${General::swroot}/fwhosts/customhosts";
|
|
my $configgrp = "${General::swroot}/fwhosts/customgroups";
|
|
my $configsrv = "${General::swroot}/fwhosts/customservices";
|
|
my $configsrvgrp = "${General::swroot}/fwhosts/customservicegrp";
|
|
my $configccdnet = "${General::swroot}/ovpn/ccd.conf";
|
|
my $configccdhost = "${General::swroot}/ovpn/ovpnconfig";
|
|
my $configipsec = "${General::swroot}/vpn/config";
|
|
my $configovpn = "${General::swroot}/ovpn/settings";
|
|
my $val;
|
|
my $field;
|
|
my $netsettings = "${General::swroot}/ethernet/settings";
|
|
|
|
&General::readhash("/var/ipfire/ethernet/settings", \%netsettings);
|
|
&General::readhash("${General::swroot}/ovpn/settings", \%ovpnsettings);
|
|
&General::readhash("${General::swroot}/vpn/settings", \%ipsecsettings);
|
|
|
|
&General::readhasharray("$confignet", \%customnetwork);
|
|
&General::readhasharray("$confighost", \%customhost);
|
|
&General::readhasharray("$configgrp", \%customgrp);
|
|
&General::readhasharray("$configccdnet", \%ccdnet);
|
|
&General::readhasharray("$configccdhost", \%ccdhost);
|
|
&General::readhasharray("$configipsec", \%ipsecconf);
|
|
&General::readhasharray("$configsrv", \%customservice);
|
|
&General::readhasharray("$configsrvgrp", \%customservicegrp);
|
|
&General::get_aliases(\%aliases);
|
|
|
|
sub get_srv_prot
|
|
{
|
|
my $val=shift;
|
|
foreach my $key (sort {$a <=> $b} keys %customservice){
|
|
if($customservice{$key}[0] eq $val){
|
|
if ($customservice{$key}[0] eq $val){
|
|
return $customservice{$key}[2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sub get_srvgrp_prot
|
|
{
|
|
my $val=shift;
|
|
my @ips=();
|
|
my $tcp;
|
|
my $udp;
|
|
my $icmp;
|
|
foreach my $key (sort {$a <=> $b} keys %customservicegrp){
|
|
if($customservicegrp{$key}[0] eq $val){
|
|
if (&get_srv_prot($customservicegrp{$key}[2]) eq 'TCP'){
|
|
$tcp=1;
|
|
}elsif(&get_srv_prot($customservicegrp{$key}[2]) eq 'UDP'){
|
|
$udp=1;
|
|
}elsif(&get_srv_prot($customservicegrp{$key}[2]) eq 'ICMP'){
|
|
$icmp=1;
|
|
}else{
|
|
#Protocols used in servicegroups
|
|
push (@ips,$customservicegrp{$key}[2]);
|
|
}
|
|
}
|
|
}
|
|
if ($tcp eq '1'){push (@ips,'TCP');}
|
|
if ($udp eq '1'){push (@ips,'UDP');}
|
|
if ($icmp eq '1'){push (@ips,'ICMP');}
|
|
my $back=join(",",@ips);
|
|
return $back;
|
|
|
|
}
|
|
sub get_srv_port
|
|
{
|
|
my $val=shift;
|
|
my $field=shift;
|
|
my $prot=shift;
|
|
foreach my $key (sort {$a <=> $b} keys %customservice){
|
|
if($customservice{$key}[0] eq $val && $customservice{$key}[2] eq $prot){
|
|
return $customservice{$key}[$field];
|
|
}
|
|
}
|
|
}
|
|
sub get_srvgrp_port
|
|
{
|
|
my $val=shift;
|
|
my $prot=shift;
|
|
my $back;
|
|
my $value;
|
|
my @ips=();
|
|
foreach my $key (sort {$a <=> $b} keys %customservicegrp){
|
|
if($customservicegrp{$key}[0] eq $val){
|
|
if ($prot ne 'ICMP'){
|
|
$value=&get_srv_port($customservicegrp{$key}[2],1,$prot);
|
|
}elsif ($prot eq 'ICMP'){
|
|
$value=&get_srv_port($customservicegrp{$key}[2],3,$prot);
|
|
}
|
|
push (@ips,$value) if ($value ne '') ;
|
|
}
|
|
}
|
|
if($prot ne 'ICMP'){
|
|
if ($#ips gt 0){$back="-m multiport --dports ";}else{$back="--dport ";}
|
|
}elsif ($prot eq 'ICMP'){
|
|
$back="--icmp-type ";
|
|
}
|
|
|
|
$back.=join(",",@ips);
|
|
return $back;
|
|
}
|
|
sub get_ipsec_net_ip
|
|
{
|
|
my $val=shift;
|
|
my $field=shift;
|
|
foreach my $key (sort {$a <=> $b} keys %ipsecconf){
|
|
if($ipsecconf{$key}[1] eq $val){
|
|
return $ipsecconf{$key}[$field];
|
|
}
|
|
}
|
|
}
|
|
sub get_ipsec_host_ip
|
|
{
|
|
my $val=shift;
|
|
my $field=shift;
|
|
foreach my $key (sort {$a <=> $b} keys %ipsecconf){
|
|
if($ipsecconf{$key}[1] eq $val){
|
|
return $ipsecconf{$key}[$field];
|
|
}
|
|
}
|
|
}
|
|
sub get_ovpn_n2n_ip
|
|
{
|
|
my $val=shift;
|
|
my $field=shift;
|
|
foreach my $key (sort {$a <=> $b} keys %ccdhost){
|
|
if($ccdhost{$key}[1] eq $val){
|
|
return $ccdhost{$key}[$field];
|
|
}
|
|
}
|
|
}
|
|
sub get_ovpn_host_ip
|
|
{
|
|
my $val=shift;
|
|
my $field=shift;
|
|
foreach my $key (sort {$a <=> $b} keys %ccdhost){
|
|
if($ccdhost{$key}[1] eq $val){
|
|
return $ccdhost{$key}[$field];
|
|
}
|
|
}
|
|
}
|
|
sub get_ovpn_net_ip
|
|
{
|
|
|
|
my $val=shift;
|
|
my $field=shift;
|
|
foreach my $key (sort {$a <=> $b} keys %ccdnet){
|
|
if($ccdnet{$key}[0] eq $val){
|
|
return $ccdnet{$key}[$field];
|
|
}
|
|
}
|
|
}
|
|
sub get_grp_ip
|
|
{
|
|
my $val=shift;
|
|
my $src=shift;
|
|
foreach my $key (sort {$a <=> $b} keys %customgrp){
|
|
if ($customgrp{$key}[0] eq $val){
|
|
&get_address($customgrp{$key}[3],$src);
|
|
}
|
|
}
|
|
|
|
}
|
|
sub get_std_net_ip
|
|
{
|
|
my $val=shift;
|
|
my $con=shift;
|
|
if ($val eq 'ALL'){
|
|
return "0.0.0.0/0.0.0.0";
|
|
}elsif($val eq 'GREEN'){
|
|
return "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}";
|
|
}elsif($val eq 'ORANGE'){
|
|
return "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}";
|
|
}elsif($val eq 'BLUE'){
|
|
return "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}";
|
|
}elsif($val eq 'RED'){
|
|
return "0.0.0.0/0 -o $con";
|
|
}elsif($val =~ /OpenVPN/i){
|
|
return "$ovpnsettings{'DOVPN_SUBNET'}";
|
|
}elsif($val =~ /IPsec/i){
|
|
return "$ipsecsettings{'RW_NET'}";
|
|
}elsif($val eq 'IPFire'){
|
|
return ;
|
|
}
|
|
}
|
|
sub get_net_ip
|
|
{
|
|
my $val=shift;
|
|
foreach my $key (sort {$a <=> $b} keys %customnetwork){
|
|
if($customnetwork{$key}[0] eq $val){
|
|
return "$customnetwork{$key}[1]/$customnetwork{$key}[2]";
|
|
}
|
|
}
|
|
}
|
|
sub get_host_ip
|
|
{
|
|
my $val=shift;
|
|
my $src=shift;
|
|
foreach my $key (sort {$a <=> $b} keys %customhost){
|
|
if($customhost{$key}[0] eq $val){
|
|
if ($customhost{$key}[1] eq 'mac' && $src eq 'src'){
|
|
return "-m mac --mac-source $customhost{$key}[2]";
|
|
}elsif($customhost{$key}[1] eq 'ip' && $src eq 'src'){
|
|
return "$customhost{$key}[2]";
|
|
}elsif($customhost{$key}[1] eq 'ip' && $src eq 'tgt'){
|
|
return "$customhost{$key}[2]";
|
|
}elsif($customhost{$key}[1] eq 'mac' && $src eq 'tgt'){
|
|
return "none";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sub get_addresses
|
|
{
|
|
my $hash = shift;
|
|
my $key = shift;
|
|
my $type = shift;
|
|
|
|
my @addresses = ();
|
|
my $addr_type;
|
|
my $value;
|
|
my $group_name;
|
|
|
|
if ($type eq "src") {
|
|
$addr_type = $$hash{$key}[3];
|
|
$value = $$hash{$key}[4];
|
|
|
|
} elsif ($type eq "tgt") {
|
|
$addr_type = $$hash{$key}[5];
|
|
$value = $$hash{$key}[6];
|
|
}
|
|
|
|
if ($addr_type ~~ ["cust_grp_src", "cust_grp_tgt"]) {
|
|
foreach my $grp (sort {$a <=> $b} keys %customgrp) {
|
|
if ($customgrp{$grp}[0] eq $value) {
|
|
my @address = &get_address($customgrp{$grp}[3], $customgrp{$grp}[2], $type);
|
|
|
|
if (@address) {
|
|
push(@addresses, @address);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
my @address = &get_address($addr_type, $value, $type);
|
|
|
|
if (@address) {
|
|
push(@addresses, @address);
|
|
}
|
|
}
|
|
|
|
return @addresses;
|
|
}
|
|
sub get_address
|
|
{
|
|
my $key = shift;
|
|
my $value = shift;
|
|
my $type = shift;
|
|
|
|
my @ret = ();
|
|
|
|
# If the user manually typed an address, we just check if it is a MAC
|
|
# address. Otherwise, we assume that it is an IP address.
|
|
if ($key ~~ ["src_addr", "tgt_addr"]) {
|
|
if (&General::validmac($value)) {
|
|
push(@ret, "-m mac --mac-source $value");
|
|
} else {
|
|
push(@ret, $value);
|
|
}
|
|
|
|
# If a default network interface (GREEN, BLUE, etc.) is selected, we
|
|
# try to get the corresponding address of the network.
|
|
} elsif ($key ~~ ["std_net_src", "std_net_tgt", "Standard Network"]) {
|
|
my $external_interface = &get_external_interface();
|
|
|
|
my $network_address = &get_std_net_ip($value, $external_interface);
|
|
if ($network_address) {
|
|
push(@ret, $network_address);
|
|
}
|
|
|
|
# Custom networks.
|
|
} elsif ($key ~~ ["cust_net_src", "cust_net_tgt", "Custom Network"]) {
|
|
my $network_address = &get_net_ip($value);
|
|
if ($network_address) {
|
|
push(@ret, $network_address);
|
|
}
|
|
|
|
# Custom hosts.
|
|
} elsif ($key ~~ ["cust_host_src", "cust_host_tgt", "Custom Host"]) {
|
|
my $host_address = &get_host_ip($value, $type);
|
|
if ($host_address) {
|
|
push(@ret, $host_address);
|
|
}
|
|
|
|
# OpenVPN networks.
|
|
} elsif ($key ~~ ["ovpn_net_src", "ovpn_net_tgt", "OpenVPN static network"]) {
|
|
my $network_address = &get_ovpn_net_ip($value, 1);
|
|
if ($network_address) {
|
|
push(@ret, $network_address);
|
|
}
|
|
|
|
# OpenVPN hosts.
|
|
} elsif ($key ~~ ["ovpn_host_src", "ovpn_host_tgt", "OpenVPN static host"]) {
|
|
my $host_address = &get_ovpn_host_ip($value, 33);
|
|
if ($host_address) {
|
|
push(@ret, $host_address);
|
|
}
|
|
|
|
# OpenVPN N2N.
|
|
} elsif ($key ~~ ["ovpn_n2n_src", "ovpn_n2n_tgt", "OpenVPN N-2-N"]) {
|
|
my $network_address = &get_ovpn_n2n_ip($value, 11);
|
|
if ($network_address) {
|
|
push(@ret, $network_address);
|
|
}
|
|
|
|
# IPsec networks.
|
|
} elsif ($key ~~ ["ipsec_net_src", "ipsec_net_tgt", "IpSec Network"]) {
|
|
my $network_address = &get_ipsec_net_ip($value, 11);
|
|
if ($network_address) {
|
|
push(@ret, $network_address);
|
|
}
|
|
|
|
# The firewall's own IP addresses.
|
|
} elsif ($key ~~ ["ipfire", "ipfire_src"]) {
|
|
# ALL
|
|
if ($value eq "ALL") {
|
|
push(@ret, "0/0");
|
|
|
|
# GREEN
|
|
} elsif ($value eq "GREEN") {
|
|
push(@ret, $netsettings{"GREEN_ADDRESS"});
|
|
|
|
# BLUE
|
|
} elsif ($value eq "BLUE") {
|
|
push(@ret, $netsettings{"BLUE_ADDRESS"});
|
|
|
|
# ORANGE
|
|
} elsif ($value eq "ORANGE") {
|
|
push(@ret, $netsettings{"ORANGE_ADDRESS"});
|
|
|
|
# RED
|
|
} elsif ($value ~~ ["RED", "RED1"]) {
|
|
my $address = &get_external_address();
|
|
if ($address) {
|
|
push(@ret, $address);
|
|
}
|
|
|
|
# Aliases
|
|
} else {
|
|
my $alias = &get_alias($value);
|
|
if ($alias) {
|
|
push(@ret, $alias);
|
|
}
|
|
}
|
|
|
|
# If nothing was selected, we assume "any".
|
|
} else {
|
|
push(@ret, "0/0");
|
|
}
|
|
|
|
return @ret;
|
|
}
|
|
sub get_external_interface()
|
|
{
|
|
open(IFACE, "/var/ipfire/red/iface") or return "";
|
|
my $iface = <IFACE>;
|
|
close(IFACE);
|
|
|
|
return $iface;
|
|
}
|
|
sub get_external_address()
|
|
{
|
|
open(ADDR, "/var/ipfire/red/local-ipaddress") or return "";
|
|
my $address = <ADDR>;
|
|
close(ADDR);
|
|
|
|
return $address;
|
|
}
|
|
sub get_alias
|
|
{
|
|
my $id = shift;
|
|
|
|
foreach my $alias (sort keys %aliases) {
|
|
if ($id eq $alias) {
|
|
return $aliases{$alias}{"IPT"};
|
|
}
|
|
}
|
|
}
|
|
|
|
sub get_nat_address {
|
|
my $zone = shift;
|
|
my $source = shift;
|
|
|
|
# Any static address of any zone.
|
|
if ($zone eq "AUTO") {
|
|
if ($source && ($source !~ m/mac/i )) {
|
|
my $firewall_ip = &get_internal_firewall_ip_address($source, 1);
|
|
if ($firewall_ip) {
|
|
return $firewall_ip;
|
|
}
|
|
|
|
$firewall_ip = &get_matching_firewall_address($source, 1);
|
|
if ($firewall_ip) {
|
|
return $firewall_ip;
|
|
}
|
|
}
|
|
|
|
return &get_external_address();
|
|
|
|
} elsif ($zone eq "RED" || $zone eq "GREEN" || $zone eq "ORANGE" || $zone eq "BLUE") {
|
|
return $netsettings{$zone . "_ADDRESS"};
|
|
|
|
} elsif ($zone ~~ ["Default IP", "ALL"]) {
|
|
return &get_external_address();
|
|
|
|
} else {
|
|
my $alias = &get_alias($zone);
|
|
unless ($alias) {
|
|
$alias = &get_external_address();
|
|
}
|
|
return $alias;
|
|
}
|
|
|
|
print_error("Could not find NAT address");
|
|
}
|
|
|
|
sub get_internal_firewall_ip_addresses
|
|
{
|
|
my $use_orange = shift;
|
|
|
|
my @zones = ("GREEN", "BLUE");
|
|
if ($use_orange) {
|
|
push(@zones, "ORANGE");
|
|
}
|
|
|
|
my @addresses = ();
|
|
for my $zone (@zones) {
|
|
next unless (exists $netsettings{$zone . "_ADDRESS"});
|
|
|
|
my $zone_address = $netsettings{$zone . "_ADDRESS"};
|
|
push(@addresses, $zone_address);
|
|
}
|
|
|
|
return @addresses;
|
|
}
|
|
sub get_matching_firewall_address
|
|
{
|
|
my $addr = shift;
|
|
my $use_orange = shift;
|
|
|
|
my ($address, $netmask) = split("/", $addr);
|
|
|
|
my @zones = ("GREEN", "BLUE");
|
|
if ($use_orange) {
|
|
push(@zones, "ORANGE");
|
|
}
|
|
|
|
foreach my $zone (@zones) {
|
|
next unless (exists $netsettings{$zone . "_ADDRESS"});
|
|
|
|
my $zone_subnet = $netsettings{$zone . "_NETADDRESS"};
|
|
my $zone_mask = $netsettings{$zone . "_NETMASK"};
|
|
|
|
if (&General::IpInSubnet($address, $zone_subnet, $zone_mask)) {
|
|
return $netsettings{$zone . "_ADDRESS"};
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
sub get_internal_firewall_ip_address
|
|
{
|
|
my $subnet = shift;
|
|
my $use_orange = shift;
|
|
|
|
my ($net_address, $net_mask) = split("/", $subnet);
|
|
if ((!$net_mask) || ($net_mask ~~ ["32", "255.255.255.255"])) {
|
|
return 0;
|
|
}
|
|
|
|
# Convert net mask into correct format for &General::IpInSubnet().
|
|
$net_mask = &General::iporsubtodec($net_mask);
|
|
|
|
my @addresses = &get_internal_firewall_ip_addresses($use_orange);
|
|
foreach my $zone_address (@addresses) {
|
|
if (&General::IpInSubnet($zone_address, $net_address, $net_mask)) {
|
|
return $zone_address;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|