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