From f9960a0c94c7b27d17190f1fee73c9a77ec49af6 Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Thu, 29 Dec 2011 13:45:35 -0800 Subject: [PATCH] Restore blacklst and blackout chains Signed-off-by: Tom Eastep --- Shorewall/Perl/Shorewall/Chains.pm | 88 +++++++++++- Shorewall/Perl/Shorewall/Misc.pm | 144 ++++++++++++++++++- Shorewall/Perl/Shorewall/Rules.pm | 221 ++++++----------------------- 3 files changed, 273 insertions(+), 180 deletions(-) diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index de1bce317..9024362b2 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -65,6 +65,7 @@ our @EXPORT = qw( dont_delete dont_move get_action_logging + add_interface_options %chain_table %helpers @@ -1496,7 +1497,7 @@ sub copy_rules( $$;$ ) { progress_message " $count rules from $chain1->{name} appended to $chain2->{name}" if $count; - unless ( --$chain1->{references}{$name2} ) { + unless ( $nojump || --$chain1->{references}{$name2} ) { delete $chain1->{references}{$name2}; delete_chain_and_references( $chain1 ) unless keys %{$chain1->{references}}; } @@ -5801,6 +5802,91 @@ sub expand_rule( $$$$$$$$$$;$ ) $diface; } +sub copy_options( $ ) { + keys %{interface_zones( shift )} == 1; +} + +sub add_interface_options( $ ) { + my $blrules = shift; + + if ( $blrules ) { + # + # Insert all interface option rules into the rules chains + # + for my $zone1 ( off_firewall_zones ) { + my @interfaces = keys %{zone_interfaces( $zone1 )}; + + for my $zone2 ( all_zones ) { + my $chainref = $filter_table->{rules_chain( $zone1, $zone2 )}; + + if ( zone_type( $zone2 ) & (FIREWALL | VSERVER ) ) { + if ( @interfaces == 1 && copy_options( $interfaces[0] ) ) { + if ( my $chain1ref = $filter_table->{input_option_chain $interfaces[0]} ) { + copy_rules $chain1ref, $chainref, 1; + $chainref->{referenced} = 1; + } + } else { + for my $interface ( @interfaces ) { + if ( my $chain1ref = $filter_table->{forward_option_chain $interface} ) { + add_ijump ( $chainref , j => $chain1ref->{name}, @interfaces > 1 ? imatch_source_dev( $interface ) : () ); + } + } + } + } else { + if ( @interfaces == 1 && copy_options( $interfaces[0] ) ) { + if ( my $chain1ref = $filter_table->{forward_option_chain $interfaces[0]} ) { + copy_rules $chain1ref, $chainref, 1; + $chainref->{referenced} = 1; + } + } else { + for my $interface ( @interfaces ) { + if ( my $chain1ref = $filter_table->{forward_option_chain $interface} ) { + add_ijump ( $chainref , j => $chain1ref->{name}, @interfaces > 1 ? imatch_source_dev( $interface ) : () ); + } + } + } + } + } + } + + for my $zone1 ( firewall_zone, vserver_zones ) { + for my $zone2 ( off_firewall_zones ) { + my $chainref = $filter_table->{rules_chain( $zone1, $zone2 )}; + my @interfaces = keys %{zone_interfaces( $zone2 )}; + + for my $interface ( @interfaces ) { + if ( my $chain1ref = $filter_table->{output_option_chain $interface} ) { + add_ijump ( $chainref , j => $chain1ref->{name}, @interfaces > 1 ? imatch_dest_dev( $interface ) : () ); + } + } + } + } + } else { + # + # Simply move the option chain rules to the interface chains + # + for my $interface ( grep $_ ne '%vserver%', all_interfaces ) { + my $chainref; + my $chain1ref; + + if ( ( $chainref = $filter_table->{input_option_chain $interface} ) && @{$chainref->{rules}} ) { + move_rules $chainref, $chain1ref = $filter_table->{input_chain $interface}; + set_interface_option( $interface, 'use_input_chain', 1 ); + } + + if ( ( $chainref = $filter_table->{forward_option_chain $interface} ) && @{$chainref->{rules}} ) { + move_rules $chainref, $chain1ref = $filter_table->{forward_chain $interface}; + set_interface_option( $interface, 'use_forward_chain' , 1 ); + } + + if ( ( $chainref = $filter_table->{output_option_chain $interface} ) && @{$chainref->{rules}} ) { + move_rules $chainref, $chain1ref = $filter_table->{output_chain $interface}; + set_interface_option( $interface, 'use_output_chain' , 1 ); + } + } + } +} + # # The following code generates the input to iptables-restore from the contents of the # @rules arrays in the chain table entries. diff --git a/Shorewall/Perl/Shorewall/Misc.pm b/Shorewall/Perl/Shorewall/Misc.pm index f4321af36..477ef55b9 100644 --- a/Shorewall/Perl/Shorewall/Misc.pm +++ b/Shorewall/Perl/Shorewall/Misc.pm @@ -200,6 +200,144 @@ sub add_rule_pair( $$$$ ) { add_jump( $chainref , $target, 0, $predicate ); } +sub setup_blacklist() { + + my $zones = find_zones_by_option 'blacklist', 'in'; + my $zones1 = find_zones_by_option 'blacklist', 'out'; + my $chainref; + my $chainref1; + my ( $level, $disposition ) = @config{'BLACKLIST_LOGLEVEL', 'BLACKLIST_DISPOSITION' }; + my $audit = $disposition =~ /^A_/; + my $target = $disposition eq 'REJECT' ? 'reject' : $disposition; + my $orig_target = $target; + + # + # We go ahead and generate the blacklist chains and jump to them, even if they turn out to be empty. That is necessary + # for 'refresh' to work properly. + # + if ( @$zones || @$zones1 ) { + $chainref = dont_delete new_standard_chain 'blacklst' if @$zones; + $chainref1 = dont_delete new_standard_chain 'blackout' if @$zones1; + + if ( supplied $level ) { + $target = ensure_blacklog_chain ( $target, $disposition, $level, $audit ); + } elsif ( $audit ) { + require_capability 'AUDIT_TARGET', "BLACKLIST_DISPOSITION=$disposition", 's'; + $target = verify_audit( $disposition ); + } + } + + BLACKLIST: + { + if ( my $fn = open_file 'blacklist' ) { + + my $first_entry = 1; + + first_entry "$doing $fn..."; + + while ( read_a_line ) { + + if ( $first_entry ) { + unless ( @$zones || @$zones1 ) { + warning_message qq(The entries in $fn have been ignored because there are no 'blacklist' zones); + close_file; + last BLACKLIST; + } + + $first_entry = 0; + } + + my ( $networks, $protocol, $ports, $options ) = split_line 'blacklist file', { networks => 0, proto => 1, port => 2, options => 3 }; + + if ( $options eq '-' ) { + $options = 'src'; + } elsif ( $options eq 'audit' ) { + $options = 'audit,src'; + } + + my ( $to, $from, $whitelist, $auditone ) = ( 0, 0, 0, 0 ); + + my @options = split_list $options, 'option'; + + for ( @options ) { + $whitelist++ if $_ eq 'whitelist'; + $auditone++ if $_ eq 'audit'; + } + + warning_message "Duplicate 'whitelist' option ignored" if $whitelist > 1; + + my $tgt = $whitelist ? 'RETURN' : $target; + + if ( $auditone ) { + fatal_error "'audit' not allowed in whitelist entries" if $whitelist; + + if ( $audit ) { + warning_message "Superfluous 'audit' option ignored"; + } else { + warning_message "Duplicate 'audit' option ignored" if $auditone > 1; + + + + $tgt = verify_audit( 'A_' . $target, $orig_target, $target ); + } + } + + for ( @options ) { + if ( $_ =~ /^(?:src|from)$/ ) { + if ( $from++ ) { + warning_message "Duplicate 'src' ignored"; + } else { + if ( @$zones ) { + expand_rule( + $chainref , + NO_RESTRICT , + do_proto( $protocol , $ports, '' ) , + $networks, + '', + '' , + $tgt , + '' , + $tgt , + '' ); + } else { + warning_message '"src" entry ignored because there are no "blacklist in" zones'; + } + } + } elsif ( $_ =~ /^(?:dst|to)$/ ) { + if ( $to++ ) { + warning_message "Duplicate 'dst' ignored"; + } else { + if ( @$zones1 ) { + expand_rule( + $chainref1 , + NO_RESTRICT , + do_proto( $protocol , $ports, '' ) , + '', + $networks, + '' , + $tgt , + '' , + $tgt , + '' ); + } else { + warning_message '"dst" entry ignored because there are no "blacklist out" zones'; + } + } + } else { + fatal_error "Invalid blacklist option($_)" unless $_ eq 'whitelist' || $_ eq 'audit'; + } + } + + progress_message " \"$currentline\" added to blacklist"; + } + + warning_message q(There are interfaces or zones with the 'blacklist' option but the 'blacklist' file is empty) if $first_entry && @$zones; + } elsif ( @$zones || @$zones1 ) { + warning_message q(There are interfaces or zones with the 'blacklist' option, but the 'blacklist' file is either missing or has zero size); + } + } +} + # # Remove instances of 'blacklist' from the passed file. # @@ -665,6 +803,8 @@ sub add_common_rules ( $ ) { if ( $upgrade ) { exit 0 unless convert_blacklist; + } else { + setup_blacklist; } $list = find_hosts_by_option 'nosmurfs'; @@ -1346,9 +1486,9 @@ sub generate_matrix() { next if @zones <= 2 && ! $zoneref->{options}{complex}; # - # Complex zone or we have more than one non-firewall zone -- create a zone forwarding chain + # Complex zone or we have more than one non-firewall zone -- process_rules created a zone forwarding chain # - my $frwd_ref = new_standard_chain zone_forward_chain( $zone ); + my $frwd_ref = $filter_table->{zone_forward_chain( $zone )}; add_ijump( $frwd_ref , j => 'MARK --set-mark ' . in_hex( $zoneref->{mark} ) . '/' . in_hex( $globals{ZONE_MASK} ) ) if $zoneref->{mark}; diff --git a/Shorewall/Perl/Shorewall/Rules.pm b/Shorewall/Perl/Shorewall/Rules.pm index e6332aae8..203be3f18 100644 --- a/Shorewall/Perl/Shorewall/Rules.pm +++ b/Shorewall/Perl/Shorewall/Rules.pm @@ -2490,125 +2490,59 @@ sub initiate_blacklist() { $blrules = 1; } -# -# Convert a pre-4.4.25 blacklist to the 4.4.25 format and process it -# -sub setup_blacklist() { - my $zones = find_zones_by_option 'blacklist', 'in'; - my $zones1 = find_zones_by_option 'blacklist', 'out'; - my ( $level, $disposition ) = @config{'BLACKLIST_LOGLEVEL', 'BLACKLIST_DISPOSITION' }; - my $audit = $disposition =~ /^A_/; - my $target = $disposition eq 'REJECT' ? 'reject' : $disposition; - my $orig_target = $target; - my @rules; - - if ( @$zones || @$zones1 ) { - if ( supplied $level ) { - $target = 'blacklog'; - } elsif ( $audit ) { - $target = verify_audit( $disposition ); - } +sub classic_blacklist() { + my $fw = firewall_zone; + my @zones = off_firewall_zones; + my @vservers = vserver_zones; + my @state = $config{BLACKLISTNEWONLY} ? $globals{UNTRACKED} ? state_imatch 'NEW,INVALID,UNTRACKED' : state_imatch 'NEW,INVALID' : (); + # + # First take care of classic blacklisting + # + for my $zone ( @zones ) { + my $zoneref = find_zone( $zone ); + my $simple = @zones <= 2 && ! $zoneref->{options}{complex}; + + if ( $zoneref->{options}{in}{blacklist} ) { + my $blackref = $filter_table->{blacklst}; + add_ijump ensure_rules_chain( rules_chain( $zone, $_ ) ) , j => $blackref , @state for firewall_zone, @vservers; - my $fn = open_file 'blacklist'; + if ( $simple ) { + # + # We won't create a zone forwarding chain for this zone so we must add blacklisting jumps to the rules chains + # + for my $zone1 ( @zones ) { + my $ruleschain = rules_chain( $zone, $zone1 ); + my $ruleschainref = $filter_table->{$ruleschain}; - first_entry( \&initiate_blacklist ); - - while ( read_a_line ) { - my ( $networks, $protocol, $ports, $options ) = split_line 'blacklist file', { networks => 0, proto => 1, port => 2, options => 3 }; - - if ( $options eq '-' ) { - $options = 'src'; - } elsif ( $options eq 'audit' ) { - $options = 'audit,src'; - } - - my ( $to, $from, $whitelist, $auditone ) = ( 0, 0, 0, 0 ); - - my @options = split_list $options, 'option'; - - for ( @options ) { - $whitelist++ if $_ eq 'whitelist'; - $auditone++ if $_ eq 'audit'; - } - - warning_message "Duplicate 'whitelist' option ignored" if $whitelist > 1; - - my $tgt = $whitelist ? 'WHITELIST' : $target; - - if ( $auditone ) { - fatal_error "'audit' not allowed in whitelist entries" if $whitelist; - - if ( $audit ) { - warning_message "Superfluous 'audit' option ignored"; - } else { - warning_message "Duplicate 'audit' option ignored" if $auditone > 1; - } - - $tgt = verify_audit( 'A_' . $target, $orig_target, $target ); - } - - for ( @options ) { - if ( $_ =~ /^(?:src|from)$/ ) { - if ( $from++ ) { - warning_message "Duplicate 'src' ignored"; - } else { - if ( @$zones ) { - push @rules, [ 'src', $tgt, $networks, $protocol, $ports ]; - } else { - warning_message '"src" entry ignored because there are no "blacklist in" zones'; - } + if ( ( $zone ne $zone1 || $ruleschainref->{referenced} ) && $ruleschainref->{policy} ne 'NONE' ) { + add_ijump( ensure_rules_chain( $ruleschain ), j => $blackref, @state ); } - } elsif ( $_ =~ /^(?:dst|to)$/ ) { - if ( $to++ ) { - warning_message "Duplicate 'dst' ignored"; - } else { - if ( @$zones1 ) { - push @rules, [ 'dst', $tgt, $networks, $protocol, $ports ]; - } else { - warning_message '"dst" entry ignored because there are no "blacklist out" zones'; - } - } - } else { - fatal_error "Invalid blacklist option($_)" unless $_ eq 'whitelist' || $_ eq 'audit'; } } } - if ( @rules ) { - for ( @rules ) { - my ( $srcdst, $tgt, $networks, $protocols, $ports ) = @$_; + if ( $zoneref->{options}{out}{blacklist} ) { + my $blackref = $filter_table->{blackout}; + add_ijump ensure_rules_chain( rules_chain( firewall_zone, $zone ) ) , j => $blackref , @state; - $tgt .= "\t\t"; + for my $zone1 ( @zones, @vservers ) { + my $ruleschain = rules_chain( $zone1, $zone ); + my $ruleschainref = $filter_table->{$ruleschain}; - my $list = $srcdst eq 'src' ? $zones : $zones1; - - for my $zone ( @$list ) { - $currentline = $tgt; - - if ( $srcdst eq 'src' ) { - if ( $networks ne '-' ) { - $currentline .= "$zone:$networks\tall\t\t"; - } else { - $currentline .= "$zone\t\t\tall\t\t"; - } - } else { - if ( $networks ne '-' ) { - $currentline .= "all\t\t\t$zone:$networks\t"; - } else { - $currentline .= "all\t\t\t$zone\t\t\t"; - } - } - - $currentline .= "\t$protocols" if $protocols ne '-'; - $currentline .= "\t$ports" if $ports ne '-'; - - process_rule; + if ( ( $zone ne $zone1 || $ruleschainref->{referenced} ) && $ruleschainref->{policy} ne 'NONE' ) { + add_ijump( ensure_rules_chain( $ruleschain ), j => $blackref, @state ); } } - } else { - warning_message q(There are interfaces or zones with the 'blacklist' option but the 'blacklist' file is empty or does not exist) unless @rules; } - return 0; + + unless ( $simple ) { + # + # Complex zone or we have more than one non-firewall zone -- create a zone forwarding chain + # + my $frwd_ref = new_standard_chain zone_forward_chain( $zone ); + + add_ijump( $frwd_ref , j => $filter_table->{blacklst}, @state ) if $zoneref->{options}{in}{blacklist}; + } } } @@ -2616,10 +2550,10 @@ sub setup_blacklist() { # Process the Rules File # sub process_rules() { + classic_blacklist; + $section = 'BLACKLIST'; - setup_blacklist; - my $fn = open_file 'blrules'; if ( $fn ) { @@ -2629,74 +2563,7 @@ sub process_rules() { $section = ''; - if ( $blrules ) { - # - # Insert all interface option rules into the rules chains - # - for my $zone1 ( off_firewall_zones ) { - my @interfaces = keys %{zone_interfaces( $zone1 )}; - - for my $zone2 ( all_zones ) { - my $chainref = $filter_table->{rules_chain( $zone1, $zone2 )}; - - if ( zone_type( $zone2 ) & (FIREWALL | VSERVER ) ) { - if ( @interfaces == 1 ) { - if ( my $chain1ref = $filter_table->{input_option_chain $interfaces[0]} ) { - push @{$chainref->{rules}} , @{$chain1ref->{rules}}; - } - } else { - for my $interface ( @interfaces ) { - if ( my $chain1ref = $filter_table->{forward_option_chain $interface} ) { - add_ijump ( $chainref , j => $chain1ref->{name}, @interfaces > 1 ? imatch_source_dev( $interface ) : () ); - } - } - } - } else { - if ( @interfaces == 1 ) { - if ( my $chain1ref = $filter_table->{forward_option_chain $interfaces[0]} ) { - push @{$chainref->{rules}} , @{$chain1ref->{rules}}; - } - } else { - for my $interface ( @interfaces ) { - if ( my $chain1ref = $filter_table->{forward_option_chain $interface} ) { - add_ijump ( $chainref , j => $chain1ref->{name}, @interfaces > 1 ? imatch_source_dev( $interface ) : () ); - } - } - } - } - } - } - - for my $zone1 ( firewall_zone, vserver_zones ) { - for my $zone2 ( off_firewall_zones ) { - my $chainref = $filter_table->{rules_chain( $zone1, $zone2 )}; - my @interfaces = keys %{zone_interfaces( $zone2 )}; - - for my $interface ( @interfaces ) { - if ( my $chain1ref = $filter_table->{output_option_chain $interface} ) { - add_ijump ( $chainref , j => $chain1ref->{name}, @interfaces > 1 ? imatch_dest_dev( $interface ) : () ); - } - } - } - } - } else { - # - # Simply copy the option chain rules into the interface chains - # - for my $interface ( grep $_ ne '%vserver%', all_interfaces ) { - if ( my $chainref = $filter_table->{input_option_chain $interface} ) { - push @{$filter_table->{input_chain $interface}->{rules}}, @{$chainref->{rules}}; - } - - if ( my $chainref = $filter_table->{forward_option_chain $interface} ) { - push @{$filter_table->{forward_chain $interface}->{rules}}, @{$chainref->{rules}}; - } - - if ( my $chainref = $filter_table->{output_option_chain $interface} ) { - push @{$filter_table->{output_chain $interface}->{rules}}, @{$chainref->{rules}}; - } - } - } + add_interface_options( $blrules || $filter_table->{blacklst} ); $fn = open_file 'rules';