From 7bbb015e8a5b2316b158adf21c7e478b57dc673b Mon Sep 17 00:00:00 2001 From: teastep Date: Thu, 15 Mar 2007 02:18:29 +0000 Subject: [PATCH] Add Policy module with nascent Actions module git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@5538 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb --- New/Shorewall/Actions.pm | 27 ++++ New/Shorewall/Policy.pm | 323 +++++++++++++++++++++++++++++++++++++ New/compiler.pl | 335 +-------------------------------------- 3 files changed, 358 insertions(+), 327 deletions(-) create mode 100644 New/Shorewall/Actions.pm create mode 100644 New/Shorewall/Policy.pm diff --git a/New/Shorewall/Actions.pm b/New/Shorewall/Actions.pm new file mode 100644 index 000000000..c12b521a3 --- /dev/null +++ b/New/Shorewall/Actions.pm @@ -0,0 +1,27 @@ +package Shorewall::Actions; +require Exporter; +use Shorewall::Common; +use Shorewall::Config; +use Shorewall::Zones; +use Shorewall::Chains; + +use strict; + +our @ISA = qw(Exporter); +our @EXPORT = qw( %usedactions %default_actions ); +our @EXPORT_OK = qw( ); +our @VERSION = 1.00; + +# +# Used Actions. Each action that is actually used has an entry with value 1. +# +our %usedactions; +# +# Default actions for each policy. +# +our %default_actions = ( DROP => 'none' , + REJECT => 'none' , + ACCEPT => 'none' , + QUEUE => 'none' ); + +1; diff --git a/New/Shorewall/Policy.pm b/New/Shorewall/Policy.pm new file mode 100644 index 000000000..bc5150c06 --- /dev/null +++ b/New/Shorewall/Policy.pm @@ -0,0 +1,323 @@ +package Shorewall::Policy; +require Exporter; +use Shorewall::Common; +use Shorewall::Config; +use Shorewall::Zones; +use Shorewall::Chains; +use Shorewall::Actions; + +use strict; + +our @ISA = qw(Exporter); +our @EXPORT = qw( validate_policy apply_policy_rules complete_standard_chain ); +our @EXPORT_OK = qw( ); +our @VERSION = 1.00; + + +# +# Create a new policy chain and return a reference to it. +# +sub new_policy_chain($$$) +{ + my ($chain, $policy, $optional) = @_; + + my $chainref = new_chain 'filter', $chain; + + $chainref->{is_policy} = 1; + $chainref->{policy} = $policy; + $chainref->{is_optional} = $optional; + $chainref->{policychain} = $chainref; +} + +# +# Set the passed chain's policychain and policy to the passed values. +# +sub set_policy_chain($$$) +{ + my ($chain1, $chainref, $policy) = @_; + + my $chainref1 = $filter_table->{$chain1}; + $chainref1 = new_chain 'filter', $chain1 unless $chainref1; + unless ( $chainref1->{policychain} ) { + $chainref1->{policychain} = $chainref; + $chainref1->{policy} = $policy; + } +} + +# +# Process the policy file +# +sub validate_policy() +{ + sub print_policy($$$$) + { + my ( $source, $dest, $policy , $chain ) = @_; + progress_message " Policy for $source to $dest is $policy using chain $chain" + unless ( $source eq $dest ) || ( $source eq 'all' ) || ( $dest eq 'all' ); + } + + my %validpolicies = ( + ACCEPT => undef, + REJECT => undef, + DROP => undef, + CONTINUE => undef, + QUEUE => undef, + NONE => undef + ); + + my %map = ( DROP_DEFAULT => 'DROP' , + REJECT_DEFAULT => 'REJECT' , + ACCEPT_DEFAULT => 'ACCEPT' , + QUEUE_DEFAULT => 'QUEUE' ); + + my $zone; + + use constant { OPTIONAL => 1 }; + + for my $option qw/DROP_DEFAULT REJECT_DEFAULT ACCEPT_DEFAULT QUEUE_DEFAULT/ { + my $action = $config{$option}; + next if $action eq 'none'; + my $actiontype = $targets{$action}; + + if ( defined $actiontype ) { + fatal_error "Invalid setting ($action) for $option" unless $actiontype & ACTION; + } else { + fatal_error "Default Action $option=$action not found"; + } + + unless ( $usedactions{$action} ) { + $usedactions{$action} = 1; + createactionchain $action; + } + + $default_actions{$map{$option}} = $action; + } + + for $zone ( @zones ) { + push @policy_chains, ( new_policy_chain "${zone}2${zone}", 'ACCEPT', OPTIONAL ); + + if ( $config{IMPLICIT_CONTINUE} && ( @{$zones{$zone}{parents}} ) ) { + for my $zone1 ( @zones ) { + next if $zone eq $zone1; + push @policy_chains, ( new_policy_chain "${zone}2${zone1}", 'CONTINUE', OPTIONAL ); + push @policy_chains, ( new_policy_chain "${zone1}2${zone}", 'CONTINUE', OPTIONAL ); + } + } + } + + open POLICY, "$ENV{TMP_DIR}/policy" or fatal_error "Unable to open stripped policy file: $!"; + + while ( $line = ) { + chomp $line; + $line =~ s/\s+/ /g; + + my ( $client, $server, $policy, $loglevel, $synparams , $extra ) = split /\s+/, $line; + + fatal_error "Invalid policy file entry: $line" if $extra; + + $loglevel = '' unless defined $loglevel; + $synparams = '' unless defined $synparams; + $loglevel = '' if $loglevel eq '-'; + $synparams = '' if $synparams eq '-'; + + my $clientwild = ( "\L$client" eq 'all' ); + + fatal_error "Undefined zone $client" unless $clientwild || $zones{$client}; + + my $serverwild = ( "\L$server" eq 'all' ); + + fatal_error "Undefined zone $server" unless $serverwild || $zones{$server}; + + ( $policy , my $default ) = split /:/, $policy; + + if ( "\L$policy" eq 'none' ) { + $default = 'none'; + } elsif ( $default ) { + my $defaulttype = $targets{$default}; + + if ( $defaulttype & ACTION ) { + unless ( $usedactions{$default} ) { + $usedactions{$default} = 1; + createactionchain $default; + } + } else { + fatal_error "Unknown Default Action ($default) in policy \"$line\""; + } + } else { + $default = $default_actions{$policy} || ''; + } + + fatal_error "Invalid policy $policy" unless exists $validpolicies{$policy}; + + if ( $policy eq 'NONE' ) { + fatal_error "$client, $server, $policy, $loglevel, $synparams: NONE policy not allowed to/from firewall zone" + if ( $zones{$client}{type} eq 'firewall' ) || ( $zones{$server}{type} eq 'firewall' ); + fatal_error "$client $server $policy $loglevel $synparams: NONE policy not allowed with \"all\"" + if $clientwild || $serverwild; + } + + my $chain = "${client}2${server}"; + my $chainref; + + if ( defined $filter_table->{$chain} ) { + $chainref = $filter_table->{$chain}; + + if ( $chainref->{is_policy} ) { + if ( $chainref->{is_optional} ) { + $chainref->{is_optional} = 0; + } else { + fatal_error "Duplicate policy: $client $server $policy"; + } + } else { + $chainref->{is_policy} = 1; + $chainref->{policy} = $policy; + $chainref->{policy_chain} = $chainref; + push @policy_chains, ( $chainref ); + } + } else { + $chainref = new_policy_chain $chain, $policy, 0; + push @policy_chains, ( $chainref ); + } + + $chainref->{loglevel} = $loglevel if $loglevel; + $chainref->{synparams} = $synparams if $synparams; + $chainref->{default} = $default if $default; + + if ( $clientwild ) { + if ( $serverwild ) { + for my $zone ( @zones , 'all' ) { + for my $zone1 ( @zones , 'all' ) { + set_policy_chain "${zone}2${zone1}", $chainref, $policy; + print_policy $zone, $zone1, $policy, $chain; + } + } + } else { + for my $zone ( @zones ) { + set_policy_chain "${zone}2${server}", $chainref, $policy; + print_policy $zone, $server, $policy, $chain; + } + } + } elsif ( $serverwild ) { + for my $zone ( @zones , 'all' ) { + set_policy_chain "${client}2${zone}", $chainref, $policy; + print_policy $client, $zone, $policy, $chain; + } + + } else { + print_policy $client, $server, $policy, $chain; + } + } + + close POLICY; +} + +# +# Policy Rule application +# +sub policy_rules( $$$$ ) { + my ( $chainref , $target, $loglevel, $default ) = @_; + + add_rule $chainref, "-j $default" if $default && $default ne 'none'; + + log_rule $loglevel , $chainref , $target , '' if $loglevel; + + fatal_error "Null target in policy_rules()" unless $target; + + add_rule $chainref , ( '-j ' . ( $target eq 'REJECT' ? 'reject' : $target ) ) unless $target eq 'CONTINUE'; +} + +sub report_syn_flood_protection() { + progress_message ' Enabled SYN flood protection'; +} + +sub default_policy( $$$ ) { + my $chainref = $_[0]; + my $policyref = $chainref->{policychain}; + my $synparams = $policyref->{synparams}; + my $default = $policyref->{default}; + my $policy = $policyref->{policy}; + my $loglevel = $policyref->{loglevel}; + + fatal_error "No default policy for $_[1] to zone $_[2]" unless $policyref; + + if ( $chainref eq $policyref ) { + policy_rules $chainref , $policy, $loglevel , $default; + } else { + if ( $policy eq 'ACCEPT' || $policy eq 'QUEUE' ) { + if ( $synparams ) { + report_syn_flood_protection; + policy_rules $chainref , $policy , $loglevel , $default; + } else { + add_rule $chainref, "-j $policyref->{name}"; + $chainref = $policyref; + } + } elsif ( $policy eq 'CONTINUE' ) { + report_syn_flood_protection if $synparams; + policy_rules $chainref , $policy , $loglevel , $default; + } else { + report_syn_flood_protection if $synparams; + add_rule $chainref , "-j $policyref->{name}"; + $chainref = $policyref; + } + } + + progress_message " Policy $policy from $_[1] to $_[2] using chain $chainref->{name}"; + +} + +sub apply_policy_rules() { + for my $chainref ( @policy_chains ) { + my $policy = $chainref->{policy}; + my $loglevel = $chainref->{loglevel}; + my $optional = $chainref->{is_optional}; + my $default = $chainref->{default}; + my $name = $chainref->{name}; + + if ( $policy ne 'NONE' ) { + if ( ! $chainref->{referenced} && ( ! $optional && $policy ne 'CONTINUE' ) ) { + ensure_filter_chain $name, 1; + } + + if ( $name =~ /^all2|2all$/ ) { + policy_rules $chainref , $policy, $loglevel , $default; + } + + } + } + + for my $zone ( @zones ) { + for my $zone1 ( @zones ) { + my $chainref = $filter_table->{"${zone}2${zone1}"}; + default_policy $chainref, $zone, $zone1 if $chainref->{referenced}; + } + } +} + +# +# Complete a standard chain +# +# - run any supplied user exit +# - search the policy file for an applicable policy and add rules as +# appropriate +# - If no applicable policy is found, add rules for an assummed +# policy of DROP INFO +# +sub complete_standard_chain ( $$$ ) { + my ( $stdchainref, $zone, $zone2 ) = @_; + + my $ruleschainref = $filter_table->{"${zone}2${zone2}"}; + my ( $policy, $loglevel, $default ) = ( 'DROP', 'info', $config{DROP_DEFAULT} ); + my $policychainref; + + $policychainref = $ruleschainref->{policychain} if $ruleschainref; + + if ( $policychainref ) { + $policy = $policychainref->{policy}; + $loglevel = $policychainref->{loglevel}; + $default = $policychainref->{default}; + } + + policy_rules $stdchainref , $policy , $loglevel, $default; +} + +1; diff --git a/New/compiler.pl b/New/compiler.pl index 16628561e..61642107c 100755 --- a/New/compiler.pl +++ b/New/compiler.pl @@ -12,6 +12,8 @@ use Shorewall::Nat; use Shorewall::Tc; use Shorewall::Tunnels; use Shorewall::Providers; +use Shorewall::Policy; +use Shorewall::Actions; # # Set to one if we find a SECTION @@ -28,10 +30,6 @@ my $sectioned = 0; # my %actions; # -# Used Actions. Each action that is actually used has an entry with value 1. -# -my %usedactions; -# # Contains an entry for each used :[:] that maps to the associated chain. # my %logactionchains; @@ -39,14 +37,6 @@ my %logactionchains; # Maps each used macro to it's 'macro. ...' file. # my %macros; -# -# Default actions for each policy. -# -my %default_actions = ( DROP => 'none' , - REJECT => 'none' , - ACCEPT => 'none' , - QUEUE => 'none' ); - # # This function determines the logging for a subordinate action or a rule within a subordinate action @@ -94,7 +84,7 @@ sub split_action ( $ ) { # # Get Macro Name # -sub isolate_action( $ ) { +sub isolate_basic_target( $ ) { ( split '/' , $_[0] )[0]; } @@ -240,46 +230,6 @@ sub merge_macro_column( $$ ) { } } -# -# Create a new policy chain and return a reference to it. -# -sub new_policy_chain($$$) -{ - my ($chain, $policy, $optional) = @_; - - my $chainref = new_chain 'filter', $chain; - - $chainref->{is_policy} = 1; - $chainref->{policy} = $policy; - $chainref->{is_optional} = $optional; - $chainref->{policychain} = $chainref; -} - -# -# Set the passed chain's policychain and policy to the passed values. -# -sub set_policy_chain($$$) -{ - my ($chain1, $chainref, $policy) = @_; - - my $chainref1 = $filter_table->{$chain1}; - $chainref1 = new_chain 'filter', $chain1 unless $chainref1; - unless ( $chainref1->{policychain} ) { - $chainref1->{policychain} = $chainref; - $chainref1->{policy} = $policy; - } -} - -# -# Display a policy -# -sub print_policy($$$$) -{ - my ( $source, $dest, $policy , $chain ) = @_; - progress_message " Policy for $source to $dest is $policy using chain $chain" - unless ( $source eq $dest ) || ( $source eq 'all' ) || ( $dest eq 'all' ); -} - # # Try to find a macro file -- RETURNS false if the file doesn't exist or MACRO if it does. # If the file exists, the macro is entered into the 'targets' table and the fully-qualified @@ -296,166 +246,6 @@ sub find_macro( $ ) } } -# -# Process the policy file -# -sub validate_policy() -{ - my %validpolicies = ( - ACCEPT => undef, - REJECT => undef, - DROP => undef, - CONTINUE => undef, - QUEUE => undef, - NONE => undef - ); - - my %map = ( DROP_DEFAULT => 'DROP' , - REJECT_DEFAULT => 'REJECT' , - ACCEPT_DEFAULT => 'ACCEPT' , - QUEUE_DEFAULT => 'QUEUE' ); - - my $zone; - - use constant { OPTIONAL => 1 }; - - for my $option qw/DROP_DEFAULT REJECT_DEFAULT ACCEPT_DEFAULT QUEUE_DEFAULT/ { - my $action = $config{$option}; - next if $action eq 'none'; - my $actiontype = $targets{$action}; - - if ( defined $actiontype ) { - fatal_error "Invalid setting ($action) for $option" unless $actiontype & ACTION; - } else { - fatal_error "Default Action/Macro $option=$action not found"; - } - - unless ( $usedactions{$action} ) { - $usedactions{$action} = 1; - createactionchain $action; - } - - $default_actions{$map{$option}} = $action; - } - - for $zone ( @zones ) { - push @policy_chains, ( new_policy_chain "${zone}2${zone}", 'ACCEPT', OPTIONAL ); - - if ( $config{IMPLICIT_CONTINUE} && ( @{$zones{$zone}{parents}} ) ) { - for my $zone1 ( @zones ) { - next if $zone eq $zone1; - push @policy_chains, ( new_policy_chain "${zone}2${zone1}", 'CONTINUE', OPTIONAL ); - push @policy_chains, ( new_policy_chain "${zone1}2${zone}", 'CONTINUE', OPTIONAL ); - } - } - } - - open POLICY, "$ENV{TMP_DIR}/policy" or fatal_error "Unable to open stripped policy file: $!"; - - while ( $line = ) { - chomp $line; - $line =~ s/\s+/ /g; - - my ( $client, $server, $policy, $loglevel, $synparams , $extra ) = split /\s+/, $line; - - fatal_error "Invalid policy file entry: $line" if $extra; - - $loglevel = '' unless defined $loglevel; - $synparams = '' unless defined $synparams; - $loglevel = '' if $loglevel eq '-'; - $synparams = '' if $synparams eq '-'; - - my $clientwild = ( "\L$client" eq 'all' ); - - fatal_error "Undefined zone $client" unless $clientwild || $zones{$client}; - - my $serverwild = ( "\L$server" eq 'all' ); - - fatal_error "Undefined zone $server" unless $serverwild || $zones{$server}; - - ( $policy , my $default ) = split /:/, $policy; - - if ( "\L$policy" eq 'none' ) { - $default = 'none'; - } elsif ( $default ) { - my $defaulttype = $targets{$default}; - - if ( $defaulttype & ACTION ) { - unless ( $usedactions{$default} ) { - $usedactions{$default} = 1; - createactionchain $default; - } - } else { - fatal_error "Unknown Default Action ($default) in policy \"$line\""; - } - } else { - $default = $default_actions{$policy} || ''; - } - - fatal_error "Invalid policy $policy" unless exists $validpolicies{$policy}; - - if ( $policy eq 'NONE' ) { - fatal_error "$client, $server, $policy, $loglevel, $synparams: NONE policy not allowed to/from firewall zone" - if ( $zones{$client}{type} eq 'firewall' ) || ( $zones{$server}{type} eq 'firewall' ); - fatal_error "$client $server $policy $loglevel $synparams: NONE policy not allowed with \"all\"" - if $clientwild || $serverwild; - } - - my $chain = "${client}2${server}"; - my $chainref; - - if ( defined $filter_table->{$chain} ) { - $chainref = $filter_table->{$chain}; - - if ( $chainref->{is_policy} ) { - if ( $chainref->{is_optional} ) { - $chainref->{is_optional} = 0; - } else { - fatal_error "Duplicate policy: $client $server $policy"; - } - } else { - $chainref->{is_policy} = 1; - $chainref->{policy} = $policy; - $chainref->{policy_chain} = $chainref; - push @policy_chains, ( $chainref ); - } - } else { - $chainref = new_policy_chain $chain, $policy, 0; - push @policy_chains, ( $chainref ); - } - - $chainref->{loglevel} = $loglevel if $loglevel; - $chainref->{synparams} = $synparams if $synparams; - $chainref->{default} = $default if $default; - - if ( $clientwild ) { - if ( $serverwild ) { - for my $zone ( @zones , 'all' ) { - for my $zone1 ( @zones , 'all' ) { - set_policy_chain "${zone}2${zone1}", $chainref, $policy; - print_policy $zone, $zone1, $policy, $chain; - } - } - } else { - for my $zone ( @zones ) { - set_policy_chain "${zone}2${server}", $chainref, $policy; - print_policy $zone, $server, $policy, $chain; - } - } - } elsif ( $serverwild ) { - for my $zone ( @zones , 'all' ) { - set_policy_chain "${client}2${zone}", $chainref, $policy; - print_policy $client, $zone, $policy, $chain; - } - - } else { - print_policy $client, $server, $policy, $chain; - } - } - - close POLICY; -} - sub process_tos() { my $chain = 'pretos'; my $stdchain = 'PREROUTING'; @@ -794,115 +584,6 @@ sub add_common_rules() { setup_forwarding; } -# -# Policy Rule application -# -sub policy_rules( $$$$ ) { - my ( $chainref , $target, $loglevel, $default ) = @_; - - add_rule $chainref, "-j $default" if $default && $default ne 'none'; - - log_rule $loglevel , $chainref , $target , '' if $loglevel; - - fatal_error "Null target in policy_rules()" unless $target; - - add_rule $chainref , ( '-j ' . ( $target eq 'REJECT' ? 'reject' : $target ) ) unless $target eq 'CONTINUE'; -} - -sub report_syn_flood_protection() { - progress_message ' Enabled SYN flood protection'; -} - -sub default_policy( $$$ ) { - my $chainref = $_[0]; - my $policyref = $chainref->{policychain}; - my $synparams = $policyref->{synparams}; - my $default = $policyref->{default}; - my $policy = $policyref->{policy}; - my $loglevel = $policyref->{loglevel}; - - fatal_error "No default policy for $_[1] to zone $_[2]" unless $policyref; - - if ( $chainref eq $policyref ) { - policy_rules $chainref , $policy, $loglevel , $default; - } else { - if ( $policy eq 'ACCEPT' || $policy eq 'QUEUE' ) { - if ( $synparams ) { - report_syn_flood_protection; - policy_rules $chainref , $policy , $loglevel , $default; - } else { - add_rule $chainref, "-j $policyref->{name}"; - $chainref = $policyref; - } - } elsif ( $policy eq 'CONTINUE' ) { - report_syn_flood_protection if $synparams; - policy_rules $chainref , $policy , $loglevel , $default; - } else { - report_syn_flood_protection if $synparams; - add_rule $chainref , "-j $policyref->{name}"; - $chainref = $policyref; - } - } - - progress_message " Policy $policy from $_[1] to $_[2] using chain $chainref->{name}"; - -} - -sub apply_policy_rules() { - for my $chainref ( @policy_chains ) { - my $policy = $chainref->{policy}; - my $loglevel = $chainref->{loglevel}; - my $optional = $chainref->{is_optional}; - my $default = $chainref->{default}; - my $name = $chainref->{name}; - - if ( $policy ne 'NONE' ) { - if ( ! $chainref->{referenced} && ( ! $optional && $policy ne 'CONTINUE' ) ) { - ensure_filter_chain $name, 1; - } - - if ( $name =~ /^all2|2all$/ ) { - policy_rules $chainref , $policy, $loglevel , $default; - } - - } - } - - for my $zone ( @zones ) { - for my $zone1 ( @zones ) { - my $chainref = $filter_table->{"${zone}2${zone1}"}; - default_policy $chainref, $zone, $zone1 if $chainref->{referenced}; - } - } -} - -# -# Complete a standard chain -# -# - run any supplied user exit -# - search the policy file for an applicable policy and add rules as -# appropriate -# - If no applicable policy is found, add rules for an assummed -# policy of DROP INFO -# -sub complete_standard_chain ( $$$ ) { - my ( $stdchainref, $zone, $zone2 ) = @_; - - my $ruleschainref = $filter_table->{"${zone}2${zone2}"}; - my ( $policy, $loglevel, $default ) = ( 'DROP', 'info', $config{DROP_DEFAULT} ); - my $policychainref; - - $policychainref = $ruleschainref->{policychain} if $ruleschainref; - - if ( $policychainref ) { - $policy = $policychainref->{policy}; - $loglevel = $policychainref->{loglevel}; - $default = $policychainref->{default}; - } - - policy_rules $stdchainref , $policy , $loglevel, $default; -} - my %maclist_targets = ( ACCEPT => { target => 'RETURN' , mangle => 1 } , REJECT => { target => 'reject' , mangle => 0 } , DROP => { target => 'DROP' , mangle => 1 } ); @@ -1068,7 +749,7 @@ sub process_macro ( $$$$$$$$$$$ ) { $mtarget = substitute_action $param, $mtarget; } - my $action = isolate_action $mtarget; + my $action = isolate_basic_target $mtarget; my $actiontype = $targets{$action}; if ( $actiontype & ACTION ) { @@ -1143,13 +824,13 @@ sub process_rule1 ( $$$$$$$$$ ) { # # Determine the validity of the action # - my $actiontype = $targets{$action} || find_macro( isolate_action $action ); + my $actiontype = $targets{$action} || find_macro( isolate_basic_target $action ); fatal_error "Unknown action ($action) in rule \"$line\"" unless $actiontype; if ( $actiontype == MACRO ) { process_macro - $macros{isolate_action $action}, $ + $macros{isolate_basic_target $action}, $ target , (split '/', $action)[1] , $source, @@ -1385,7 +1066,7 @@ sub process_rule ( $$$$$$$$$ ) { } } - my $action = isolate_action $target; + my $action = isolate_basic_target $target; $optimize = 0 if $action =~ /!^/; @@ -1527,7 +1208,7 @@ sub process_action3( $$$$$ ) { my ( $action2 , $level2 ) = split_action $target2; - my $action2type = $targets{isolate_action $action2}; + my $action2type = $targets{isolate_basic_target $action2}; unless ( $action2type == STANDARD ) { if ( $target eq 'COMMENT' ) {