9b7f55c9d66e0ceb930a5778efa320da3e3f8609
[ipfire-2.x.git] / config / firewall / firewall-lib.pl
1 #!/usr/bin/perl
2 ###############################################################################
3 #                                                                             #
4 # IPFire.org - A linux based firewall                                         #
5 # Copyright (C) 2013 Alexander Marx <amarx@ipfire.org>                        #
6 #                                                                             #
7 # This program is free software: you can redistribute it and/or modify        #
8 # it under the terms of the GNU General Public License as published by        #
9 # the Free Software Foundation, either version 3 of the License, or           #
10 # (at your option) any later version.                                         #
11 #                                                                             #
12 # This program is distributed in the hope that it will be useful,             #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of              #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
15 # GNU General Public License for more details.                                #
16 #                                                                             #
17 # You should have received a copy of the GNU General Public License           #
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
19 #                                                                             #
20 ###############################################################################
21
22 use strict;
23 no warnings 'uninitialized';
24
25 package fwlib;
26
27 my %customnetwork=();
28 my %customhost=();
29 my %customgrp=();
30 my %customgeoipgrp=();
31 my %customservice=();
32 my %customservicegrp=();
33 my %ccdnet=();
34 my %ccdhost=();
35 my %ipsecconf=();
36 my %ipsecsettings=();
37 my %netsettings=();
38 my %ovpnsettings=();
39 my %aliases=();
40
41 require '/var/ipfire/general-functions.pl';
42
43 my $confignet           = "${General::swroot}/fwhosts/customnetworks";
44 my $confighost          = "${General::swroot}/fwhosts/customhosts";
45 my $configgrp           = "${General::swroot}/fwhosts/customgroups";
46 my $configgeoipgrp      = "${General::swroot}/fwhosts/customgeoipgrp";
47 my $configsrv           = "${General::swroot}/fwhosts/customservices";
48 my $configsrvgrp        = "${General::swroot}/fwhosts/customservicegrp";
49 my $configccdnet        = "${General::swroot}/ovpn/ccd.conf";
50 my $configccdhost       = "${General::swroot}/ovpn/ovpnconfig";
51 my $configipsec         = "${General::swroot}/vpn/config";
52 my $configovpn          = "${General::swroot}/ovpn/settings";
53 my $val;
54 my $field;
55 my $netsettings         = "${General::swroot}/ethernet/settings";
56
57 &General::readhash("/var/ipfire/ethernet/settings", \%netsettings);
58 &General::readhash("${General::swroot}/ovpn/settings", \%ovpnsettings);
59 &General::readhash("${General::swroot}/vpn/settings", \%ipsecsettings);
60
61 &General::readhasharray("$confignet", \%customnetwork);
62 &General::readhasharray("$confighost", \%customhost);
63 &General::readhasharray("$configgrp", \%customgrp);
64 &General::readhasharray("$configgeoipgrp", \%customgeoipgrp);
65 &General::readhasharray("$configccdnet", \%ccdnet);
66 &General::readhasharray("$configccdhost", \%ccdhost);
67 &General::readhasharray("$configipsec", \%ipsecconf);
68 &General::readhasharray("$configsrv", \%customservice);
69 &General::readhasharray("$configsrvgrp", \%customservicegrp);
70 &General::get_aliases(\%aliases);
71
72 sub get_srv_prot
73 {
74         my $val=shift;
75         foreach my $key (sort {$a <=> $b} keys %customservice){
76                 if($customservice{$key}[0] eq $val){
77                         if ($customservice{$key}[0] eq $val){
78                                 return $customservice{$key}[2];
79                         }
80                 }
81         }
82 }
83 sub get_srvgrp_prot
84 {
85         my $val=shift;
86         my @ips=();
87         my $tcp;
88         my $udp;
89         my $icmp;
90         foreach my $key (sort {$a <=> $b} keys %customservicegrp){
91                 if($customservicegrp{$key}[0] eq $val){
92                         if (&get_srv_prot($customservicegrp{$key}[2]) eq 'TCP'){ 
93                                 $tcp=1;
94                         }elsif(&get_srv_prot($customservicegrp{$key}[2]) eq 'UDP'){ 
95                                 $udp=1;
96                         }elsif(&get_srv_prot($customservicegrp{$key}[2]) eq 'ICMP'){
97                                 $icmp=1;
98                         }else{
99                                 #Protocols used in servicegroups
100                                 push (@ips,$customservicegrp{$key}[2]);
101                         }
102                 }
103         }
104         if ($tcp eq '1'){push (@ips,'TCP');}
105         if ($udp eq '1'){push (@ips,'UDP');}
106         if ($icmp eq '1'){push (@ips,'ICMP');}
107         my $back=join(",",@ips);
108         return $back;
109         
110 }
111 sub get_srv_port
112 {
113         my $val=shift;
114         my $field=shift;
115         my $prot=shift;
116         foreach my $key (sort {$a <=> $b} keys %customservice){
117                 if($customservice{$key}[0] eq $val && $customservice{$key}[2] eq $prot){
118                         return $customservice{$key}[$field];
119                 }
120         }
121 }
122 sub get_srvgrp_port
123 {
124         my $val=shift;
125         my $prot=shift;
126         my $back;
127         my $value;
128         my @ips=();
129         foreach my $key (sort {$a <=> $b} keys %customservicegrp){
130                 if($customservicegrp{$key}[0] eq $val){
131                         if ($prot ne 'ICMP'){
132                                 $value=&get_srv_port($customservicegrp{$key}[2],1,$prot);
133                         }elsif ($prot eq 'ICMP'){
134                                 $value=&get_srv_port($customservicegrp{$key}[2],3,$prot);
135                         }
136                         push (@ips,$value) if ($value ne '') ;
137                 }
138         }
139         if($prot ne 'ICMP'){
140                 if ($#ips gt 0){$back="-m multiport --dports ";}else{$back="--dport ";}
141         }elsif ($prot eq 'ICMP'){
142                 $back="--icmp-type ";
143         }
144         
145         $back.=join(",",@ips);
146         return $back;
147 }
148 sub get_ipsec_net_ip
149 {
150         my $val=shift;
151         my $field=shift;
152         foreach my $key (sort {$a <=> $b} keys %ipsecconf){
153                 #adapt $val to reflect real name without subnet (if rule with only one ipsec subnet is created)
154                 my @tmpval = split (/\|/, $val);
155                 $val = $tmpval[0];
156                 if($ipsecconf{$key}[1] eq $val){
157                         return $ipsecconf{$key}[$field];
158                 }
159         }
160 }
161 sub get_ipsec_host_ip
162 {
163         my $val=shift;
164         my $field=shift;
165         foreach my $key (sort {$a <=> $b} keys %ipsecconf){
166                 if($ipsecconf{$key}[1] eq $val){
167                         return $ipsecconf{$key}[$field];
168                 }
169         }
170 }
171 sub get_ovpn_n2n_ip
172 {
173         my $val=shift;
174         my $field=shift;
175         foreach my $key (sort {$a <=> $b} keys %ccdhost){
176                 if($ccdhost{$key}[1] eq $val){
177                         return $ccdhost{$key}[$field];
178                 }
179         }
180 }
181 sub get_ovpn_host_ip
182 {
183         my $val=shift;
184         my $field=shift;
185         foreach my $key (sort {$a <=> $b} keys %ccdhost){
186                 if($ccdhost{$key}[1] eq $val){
187                         return $ccdhost{$key}[$field];
188                 }
189         }
190 }
191 sub get_ovpn_net_ip
192 {
193         
194         my $val=shift;
195         my $field=shift;
196         foreach my $key (sort {$a <=> $b} keys %ccdnet){
197                 if($ccdnet{$key}[0] eq $val){
198                         return $ccdnet{$key}[$field];
199                 }
200         }
201 }
202 sub get_grp_ip
203 {
204         my $val=shift;
205         my $src=shift;
206         foreach my $key (sort {$a <=> $b} keys %customgrp){
207                 if ($customgrp{$key}[0] eq $val){
208                         &get_address($customgrp{$key}[3],$src);
209                 }
210         }               
211         
212 }
213 sub get_std_net_ip
214 {
215         my $val=shift;
216         my $con=shift;
217         if ($val eq 'ALL'){
218                 return "0.0.0.0/0.0.0.0";
219         }elsif($val eq 'GREEN'){
220                 return "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}";
221         }elsif($val eq 'ORANGE'){
222                 return "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}";
223         }elsif($val eq 'BLUE'){
224                 return "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}";
225         }elsif($val eq 'RED'){
226                 return "0.0.0.0/0";
227         }elsif($val =~ /OpenVPN/i){
228                 return "$ovpnsettings{'DOVPN_SUBNET'}";
229         }elsif($val =~ /IPsec/i){
230                 return "$ipsecsettings{'RW_NET'}";
231         }elsif($val eq 'IPFire'){
232                 return ;
233         }
234 }
235 sub get_interface
236 {
237         my $net=shift;
238         if($net eq "$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}"){
239                 return "$netsettings{'GREEN_DEV'}";
240         }
241         if($net eq "$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}"){
242                 return "$netsettings{'ORANGE_DEV'}";
243         }
244         if($net eq "$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}"){
245                 return "$netsettings{'BLUE_DEV'}";
246         }
247         if($net eq "0.0.0.0/0") {
248                 return &get_external_interface();
249         }
250         return "";
251 }
252 sub get_net_ip
253 {
254         my $val=shift;
255         foreach my $key (sort {$a <=> $b} keys %customnetwork){
256                 if($customnetwork{$key}[0] eq $val){
257                         return "$customnetwork{$key}[1]/$customnetwork{$key}[2]";
258                 }  
259         }
260 }
261 sub get_host_ip
262 {
263         my $val=shift;
264         my $src=shift;
265         foreach my $key (sort {$a <=> $b} keys %customhost){
266                 if($customhost{$key}[0] eq $val){
267                         if ($customhost{$key}[1] eq 'mac' && $src eq 'src'){
268                         return "-m mac --mac-source $customhost{$key}[2]";
269                         }elsif($customhost{$key}[1] eq 'ip' && $src eq 'src'){
270                                 return "$customhost{$key}[2]";
271                         }elsif($customhost{$key}[1] eq 'ip' && $src eq 'tgt'){
272                                 return "$customhost{$key}[2]";
273                         }elsif($customhost{$key}[1] eq 'mac' && $src eq 'tgt'){
274                                 return "none";
275                         }
276                 }  
277         }
278 }
279 sub get_addresses
280 {
281         my $hash = shift;
282         my $key  = shift;
283         my $type = shift;
284
285         my @addresses = ();
286         my $addr_type;
287         my $value;
288         my $group_name;
289
290         if ($type eq "src") {
291                 $addr_type = $$hash{$key}[3];
292                 $value = $$hash{$key}[4];
293
294         } elsif ($type eq "tgt") {
295                 $addr_type = $$hash{$key}[5];
296                 $value = $$hash{$key}[6];
297         }
298
299         if ($addr_type ~~ ["cust_grp_src", "cust_grp_tgt"]) {
300                 foreach my $grp (sort {$a <=> $b} keys %customgrp) {
301                         if ($customgrp{$grp}[0] eq $value) {
302                                 my @address = &get_address($customgrp{$grp}[3], $customgrp{$grp}[2], $type);
303
304                                 if (@address) {
305                                         push(@addresses, @address);
306                                 }
307                         }
308                 }
309         }elsif ($addr_type ~~ ["cust_geoip_src", "cust_geoip_tgt"] && $value =~ "group:") {
310                 $value=substr($value,6);
311                 foreach my $grp (sort {$a <=> $b} keys %customgeoipgrp) {
312                         if ($customgeoipgrp{$grp}[0] eq $value) {
313                                 my @address = &get_address($addr_type, $customgeoipgrp{$grp}[2], $type);
314
315                                 if (@address) {
316                                         push(@addresses, @address);
317                                 }
318                         }
319                 }
320         } else {
321                 my @address = &get_address($addr_type, $value, $type);
322
323                 if (@address) {
324                         push(@addresses, @address);
325                 }
326         }
327
328         return @addresses;
329 }
330 sub get_address
331 {
332         my $key   = shift;
333         my $value = shift;
334         my $type  = shift;
335
336         my @ret = ();
337
338         # If the user manually typed an address, we just check if it is a MAC
339         # address. Otherwise, we assume that it is an IP address.
340         if ($key ~~ ["src_addr", "tgt_addr"]) {
341                 if (&General::validmac($value)) {
342                         push(@ret, ["-m mac --mac-source $value", ""]);
343                 } else {
344                         push(@ret, [$value, ""]);
345                 }
346
347         # If a default network interface (GREEN, BLUE, etc.) is selected, we
348         # try to get the corresponding address of the network.
349         } elsif ($key ~~ ["std_net_src", "std_net_tgt", "Standard Network"]) {
350                 my $external_interface = &get_external_interface();
351
352                 my $network_address = &get_std_net_ip($value, $external_interface);
353
354                 if ($network_address) {
355                         my $interface = &get_interface($network_address);
356                         push(@ret, [$network_address, $interface]);
357                 }
358
359         # Custom networks.
360         } elsif ($key ~~ ["cust_net_src", "cust_net_tgt", "Custom Network"]) {
361                 my $network_address = &get_net_ip($value);
362                 if ($network_address) {
363                         push(@ret, [$network_address, ""]);
364                 }
365
366         # Custom hosts.
367         } elsif ($key ~~ ["cust_host_src", "cust_host_tgt", "Custom Host"]) {
368                 my $host_address = &get_host_ip($value, $type);
369                 if ($host_address) {
370                         push(@ret, [$host_address, ""]);
371                 }
372
373         # OpenVPN networks.
374         } elsif ($key ~~ ["ovpn_net_src", "ovpn_net_tgt", "OpenVPN static network"]) {
375                 my $network_address = &get_ovpn_net_ip($value, 1);
376                 if ($network_address) {
377                         push(@ret, [$network_address, ""]);
378                 }
379
380         # OpenVPN hosts.
381         } elsif ($key ~~ ["ovpn_host_src", "ovpn_host_tgt", "OpenVPN static host"]) {
382                 my $host_address = &get_ovpn_host_ip($value, 33);
383                 if ($host_address) {
384                         push(@ret, [$host_address, ""]);
385                 }
386
387         # OpenVPN N2N.
388         } elsif ($key ~~ ["ovpn_n2n_src", "ovpn_n2n_tgt", "OpenVPN N-2-N"]) {
389                 my $network_address = &get_ovpn_n2n_ip($value, 11);
390                 if ($network_address) {
391                         push(@ret, [$network_address, ""]);
392                 }
393
394         # IPsec networks.
395         } elsif ($key ~~ ["ipsec_net_src", "ipsec_net_tgt", "IpSec Network"]) {
396                 #Check if we have multiple subnets and only want one of them
397                 if ( $value =~ /\|/ ){
398                         my @parts = split(/\|/, $value);
399                         push(@ret, [$parts[1], ""]);
400                 }else{
401                         my $network_address = &get_ipsec_net_ip($value, 11);
402                         my @nets = split(/\|/, $network_address);
403                         foreach my $net (@nets) {
404                                 push(@ret, [$net, ""]);
405                         }
406                 }
407
408         # The firewall's own IP addresses.
409         } elsif ($key ~~ ["ipfire", "ipfire_src"]) {
410                 # ALL
411                 if ($value eq "ALL") {
412                         push(@ret, ["0/0", ""]);
413
414                 # GREEN
415                 } elsif ($value eq "GREEN") {
416                         push(@ret, [$netsettings{"GREEN_ADDRESS"}, ""]);
417
418                 # BLUE
419                 } elsif ($value eq "BLUE") {
420                         push(@ret, [$netsettings{"BLUE_ADDRESS"}, ""]);
421
422                 # ORANGE
423                 } elsif ($value eq "ORANGE") {
424                         push(@ret, [$netsettings{"ORANGE_ADDRESS"}, ""]);
425
426                 # RED
427                 } elsif ($value ~~ ["RED", "RED1"]) {
428                         my $address = &get_external_address();
429                         if ($address) {
430                                 push(@ret, [$address, ""]);
431                         }
432
433                 # Aliases
434                 } else {
435                         my $alias = &get_alias($value);
436                         if ($alias) {
437                                 push(@ret, [$alias, ""]);
438                         }
439                 }
440
441         # Handle rule options with GeoIP as source.
442         } elsif ($key eq "cust_geoip_src") {
443                 # Get external interface.
444                 my $external_interface = &get_external_interface();
445
446                 push(@ret, ["-m geoip --src-cc $value", "$external_interface"]);
447
448         # Handle rule options with GeoIP as target.
449         } elsif ($key eq "cust_geoip_tgt") {
450                 # Get external interface.
451                 my $external_interface = &get_external_interface();
452
453                 push(@ret, ["-m geoip --dst-cc $value", "$external_interface"]);
454
455         # If nothing was selected, we assume "any".
456         } else {
457                 push(@ret, ["0/0", ""]);
458         }
459
460         return @ret;
461 }
462 sub get_external_interface()
463 {
464         open(IFACE, "/var/ipfire/red/iface") or return "";
465         my $iface = <IFACE>;
466         close(IFACE);
467
468         return $iface;
469 }
470 sub get_external_address()
471 {
472         open(ADDR, "/var/ipfire/red/local-ipaddress") or return "";
473         my $address = <ADDR>;
474         close(ADDR);
475
476         return $address;
477 }
478 sub get_alias
479 {
480         my $id = shift;
481
482         foreach my $alias (sort keys %aliases) {
483                 if ($id eq $alias) {
484                         return $aliases{$alias}{"IPT"};
485                 }
486         }
487 }
488
489 sub get_nat_address {
490         my $zone = shift;
491         my $source = shift;
492
493         # Any static address of any zone.
494         if ($zone eq "AUTO") {
495                 if ($source && ($source !~ m/mac/i )) {
496                         my $firewall_ip = &get_internal_firewall_ip_address($source, 1);
497                         if ($firewall_ip) {
498                                 return $firewall_ip;
499                         }
500
501                         $firewall_ip = &get_matching_firewall_address($source, 1);
502                         if ($firewall_ip) {
503                                 return $firewall_ip;
504                         }
505                 }
506
507                 return &get_external_address();
508
509         } elsif ($zone eq "RED" || $zone eq "GREEN" || $zone eq "ORANGE" || $zone eq "BLUE") {
510                 return $netsettings{$zone . "_ADDRESS"};
511
512         } elsif ($zone ~~ ["Default IP", "ALL"]) {
513                 return &get_external_address();
514
515         } else {
516                 my $alias = &get_alias($zone);
517                 unless ($alias) {
518                         $alias = &get_external_address();
519                 }
520                 return $alias;
521         }
522
523         print_error("Could not find NAT address");
524 }
525
526 sub get_internal_firewall_ip_addresses
527 {
528         my $use_orange = shift;
529
530         my @zones = ("GREEN", "BLUE");
531         if ($use_orange) {
532                 push(@zones, "ORANGE");
533         }
534
535         my @addresses = ();
536         for my $zone (@zones) {
537                 next unless (exists $netsettings{$zone . "_ADDRESS"});
538
539                 my $zone_address = $netsettings{$zone . "_ADDRESS"};
540                 push(@addresses, $zone_address);
541         }
542
543         return @addresses;
544 }
545 sub get_matching_firewall_address
546 {
547         my $addr = shift;
548         my $use_orange = shift;
549
550         my ($address, $netmask) = split("/", $addr);
551
552         my @zones = ("GREEN", "BLUE");
553         if ($use_orange) {
554                 push(@zones, "ORANGE");
555         }
556
557         foreach my $zone (@zones) {
558                 next unless (exists $netsettings{$zone . "_ADDRESS"});
559
560                 my $zone_subnet = $netsettings{$zone . "_NETADDRESS"};
561                 my $zone_mask   = $netsettings{$zone . "_NETMASK"};
562
563                 if (&General::IpInSubnet($address, $zone_subnet, $zone_mask)) {
564                         return $netsettings{$zone . "_ADDRESS"};
565                 }
566         }
567
568         return 0;
569 }
570 sub get_internal_firewall_ip_address
571 {
572         my $subnet = shift;
573         my $use_orange = shift;
574
575         my ($net_address, $net_mask) = split("/", $subnet);
576         if ((!$net_mask) || ($net_mask ~~ ["32", "255.255.255.255"])) {
577                 return 0;
578         }
579
580         # Convert net mask into correct format for &General::IpInSubnet().
581         $net_mask = &General::iporsubtodec($net_mask);
582
583         my @addresses = &get_internal_firewall_ip_addresses($use_orange);
584         foreach my $zone_address (@addresses) {
585                 if (&General::IpInSubnet($zone_address, $net_address, $net_mask)) {
586                         return $zone_address;
587                 }
588         }
589
590         return 0;
591 }
592
593 sub get_geoip_locations() {
594         # Path to the directory which contains the binary geoip
595         # databases.
596         my $directory="/usr/share/xt_geoip/LE";
597
598         # Array to store the final country list.
599         my @country_codes = ();
600
601         # Open location and do a directory listing.
602         opendir(DIR, "$directory");
603         my @locations = readdir(DIR);
604         closedir(DIR);
605
606         # Loop through the directory listing, and cut of the file extensions.
607         foreach my $location (sort @locations) {
608                 # skip . and ..
609                 next if($location =~ /^\.$/);
610                 next if($location =~ /^\.\.$/);
611
612                 # Remove whitespaces.
613                 chomp($location);
614
615                 # Cut-off file extension.
616                 my ($country_code, $extension) = split(/\./, $location);
617
618                 # Add country code to array.
619                 push(@country_codes, $country_code);
620         }
621
622         # Return final array.
623         return @country_codes;
624 }
625
626 return 1;