diff --git a/itsecur-firewall/CbButton.class b/itsecur-firewall/CbButton.class new file mode 100644 index 000000000..5f15e39b4 Binary files /dev/null and b/itsecur-firewall/CbButton.class differ diff --git a/itsecur-firewall/CbButton.java b/itsecur-firewall/CbButton.java new file mode 100644 index 000000000..97707513e --- /dev/null +++ b/itsecur-firewall/CbButton.java @@ -0,0 +1,264 @@ +import java.awt.*; +import java.util.*; + +public class CbButton extends Canvas +{ + public static final int LEFT = 0; + public static final int RIGHT = 1; + public static final int ABOVE = 2; + public static final int BELOW = 3; + + Image image; + String string; + CbButtonCallback callback; + int imode; + int iwidth, iheight, pwidth, pheight, twidth, theight; + boolean inside, indent; + + CbButtonGroup group; + boolean selected; + + Color lc1 = Util.light_edge, lc2 = Util.body, lc3 = Util.dark_edge; + Color hc1 = Util.light_edge_hi, hc2 = Util.body_hi, hc3 = Util.dark_edge_hi; + + public CbButton(Image i, CbButtonCallback cb) + { + this(i, null, LEFT, cb); + } + + public CbButton(String s, CbButtonCallback cb) + { + this(null, s, LEFT, cb); + } + + public CbButton(Image i, String s, int im, CbButtonCallback cb) + { + image = i; + string = s; + imode = im; + callback = cb; + if (image != null) { + iwidth = Util.getWidth(image); + iheight = Util.getHeight(image); + } + if (string != null) { + twidth = Util.fnm.stringWidth(string); + theight = Util.fnm.getHeight(); + } + if (image != null && string != null) { + switch(imode) { + case LEFT: + case RIGHT: + pwidth = iwidth + twidth + 6; + pheight = Math.max(iheight , theight) + 4; + break; + case ABOVE: + case BELOW: + pwidth = Math.max(iwidth, twidth) + 4; + pheight = iheight + theight + 6; + break; + } + } + else if (image != null) { + pwidth = iwidth + 4; + pheight = iheight + 4; + } + else if (string != null) { + pwidth = twidth + 8; + pheight = theight + 8; + } + } + + /**Make this button part of a mutual-exclusion group. Only one such + * button can be indented at a time + */ + public void setGroup(CbButtonGroup g) + { + group = g; + group.add(this); + } + + /**Make this button the selected one in it's group + */ + public void select() + { + if (group != null) + group.select(this); + } + + /**Display the given string + */ + public void setText(String s) + { + string = s; + image = null; + twidth = Util.fnm.stringWidth(string); + theight = Util.fnm.getHeight(); + repaint(); + } + + /**Display the given image + */ + public void setImage(Image i) + { + string = null; + image = i; + iwidth = Util.getWidth(image); + iheight = Util.getHeight(image); + repaint(); + } + + /**Display the given image and text, with the given alignment mode + */ + public void setImageText(Image i, String s, int m) + { + image = i; + string = s; + imode = m; + twidth = Util.fnm.stringWidth(string); + theight = Util.fnm.getHeight(); + iwidth = Util.getWidth(image); + iheight = Util.getHeight(image); + repaint(); + } + + public void paint(Graphics g) + { + Color c1 = inside ? hc1 : lc1, + c2 = inside ? hc2 : lc2, + c3 = inside ? hc3 : lc3; + int w = size().width, h = size().height; + Color hi = indent||selected ? c3 : c1, + lo = indent||selected ? c1 : c3; + g.setColor(c2); + g.fillRect(0, 0, w-1, h-1); + g.setColor(hi); + g.drawLine(0, 0, w-2, 0); + g.drawLine(0, 0, 0, h-2); + g.setColor(lo); + g.drawLine(w-1, h-1, w-1, 1); + g.drawLine(w-1, h-1, 1, h-1); + if (inside) { + /* g.setColor(hi); + g.drawLine(1, 1, w-3, 1); + g.drawLine(1, 1, 1, h-3); */ + g.setColor(lo); + g.drawLine(w-2, h-2, w-2, 2); + g.drawLine(w-2, h-2, 2, h-2); + } + + g.setColor(c3); + g.setFont(Util.f); + if (image != null && string != null) { + if (imode == LEFT) { + Dimension is = imgSize(w-twidth-6, h-4); + g.drawImage(image, (w - is.width - twidth - 2)/2, + (h-is.height)/2, is.width, is.height, this); + g.drawString(string, + (w - is.width - twidth - 2)/2 +is.width +2, + (h + theight - Util.fnm.getDescent())/2); + } + else if (imode == RIGHT) { + } + else if (imode == ABOVE) { + //Dimension is = imgSize(w-4, h-theight-6); + g.drawImage(image, (w - iwidth)/2, + (h - iheight - theight - 2)/2, + iwidth, iheight, this); + g.drawString(string, (w - twidth)/2, iheight+Util.fnm.getHeight()+2); + } + else if (imode == BELOW) { + } + } + else if (image != null) { + Dimension is = imgSize(w-4, h-4); + g.drawImage(image, (w - is.width)/2, (h-is.height)/2, + is.width, is.height, this); + } + else if (string != null) { + g.drawString(string, (w - twidth)/2, + (h+theight-Util.fnm.getDescent())/2); + } + } + + public void update(Graphics g) { paint(g); } + + public boolean mouseEnter(Event e, int x, int y) + { + inside = true; + repaint(); + return true; + } + + public boolean mouseExit(Event e, int x, int y) + { + inside = false; + repaint(); + return true; + } + + public boolean mouseDown(Event e, int x, int y) + { + indent = true; + repaint(); + return true; + } + + public boolean mouseUp(Event e, int x, int y) + { + if (x >= 0 && y >= 0 && x < size().width && y < size().height) { + if (callback != null) + callback.click(this); + select(); + } + indent = false; + repaint(); + return true; + } + + public Dimension preferredSize() + { + return new Dimension(pwidth, pheight); + } + + public Dimension minimumSize() + { + return preferredSize(); + } + + private Dimension imgSize(int mw, int mh) + { + float ws = (float)mw/(float)iwidth, + hs = (float)mh/(float)iheight; + float s = ws < hs ? ws : hs; + if (s > 1) s = 1; + return new Dimension((int)(iwidth*s), (int)(iheight*s)); + } +} + + +interface CbButtonCallback +{ + void click(CbButton b); +} + + +class CbButtonGroup +{ + Vector buttons = new Vector(); + + void add(CbButton b) + { + buttons.addElement(b); + } + + void select(CbButton b) + { + for(int i=0; i x2 ? x2 : i+s, y1); + for(i=y1; i<=y2; i+=s2) + g.drawLine(x2, i, x2, i+s > y2 ? y2 : i+s); + for(i=x2; i>=x1; i-=s2) + g.drawLine(i, y2, i-s < x1 ? x1 : i-s, y2); + for(i=y2; i>=y1; i-=s2) + g.drawLine(x1, i, x1, i-s < y1 ? y1 : i-s); + } + + static void recursiveLayout(Container c) + { + c.layout(); + for(int i=0; i{'edit'})) { + if ($_[0]->{'edit'}) { + @edit = @read = split(/\s+/, $_[0]->{'features'}); + } + else { + @read = split(/\s+/, $_[0]->{'features'}); + } + } +else { + @edit = split(/\s+/, $_[0]->{'features'}); + @read = split(/\s+/, $_[0]->{'rfeatures'}); + } + +local $w; +foreach $w ([ \@edit, "features", "all" ], + [ \@read, "rfeatures", "rall" ]) { + local %can = map { $_, 1 } @{$w->[0]}; + print " ",$text{'acl_'.$w->[1]}, + " \n"; + printf "[2] value=1 %s> %s\n", + $can{"*"} ? "checked" : "", $text{'acl_all'}; + printf "[2] value=0 %s> %s
\n", + $can{"*"} ? "" : "checked", $text{'acl_sel'}; + printf " \n"; + } +} + +# acl_security_save(&options) +# Parse the form for security options for the acl module +sub acl_security_save +{ +$_[0]->{'features'} = $in{'all'} ? "*" : + join(" ", split(/\0/, $in{'features'})); +$_[0]->{'rfeatures'} = $in{'rall'} ? "*" : + join(" ", split(/\0/, $in{'rfeatures'})); +delete($_[0]->{'edit'}); +} + +1; + diff --git a/itsecur-firewall/apply.cgi b/itsecur-firewall/apply.cgi new file mode 100755 index 000000000..b0dfc9894 --- /dev/null +++ b/itsecur-firewall/apply.cgi @@ -0,0 +1,29 @@ +#!/usr/bin/perl +# apply.cgi +# Apply the firewall configuration + +require './itsecur-lib.pl'; +&can_edit_error("apply"); +&ReadParse(); +&header($text{'apply_title'}, "", + undef, undef, undef, undef, &apply_button()); +print "
\n"; + +print "

$text{'apply_doing'}
\n"; +&enable_routing(); +$err = &apply_rules(); +if ($err) { + print &text('apply_failed', $err),"

\n"; + } +else { + print "$text{'apply_done'}

\n"; + } + +print "


\n"; +if ($in{'return'}) { + &footer($ENV{'HTTP_REFERER'}, $text{'apply_return'}); + } +else { + &footer("", $text{'index_return'}); + } +&remote_webmin_log("apply"); diff --git a/itsecur-firewall/apply.pl b/itsecur-firewall/apply.pl new file mode 100755 index 000000000..d0ec49a8c --- /dev/null +++ b/itsecur-firewall/apply.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl +# apply.pl +# Apply the firewall configuration + +$ENV{'WEBMIN_CONFIG'} ||= "/etc/webmin"; +$ENV{'WEBMIN_VAR'} ||= "/var/webmin"; +$no_acl_check++; +if ($0 =~ /^(.*\/)[^\/]+$/) { + chdir($1); + } +require './itsecur-lib.pl'; +$module_name eq 'itsecur-firewall' || die "Command must be run with full path"; + +print "$text{'apply_doing'}\n"; +&enable_routing(); +$err = &apply_rules(); +if ($err) { + print &text('apply_failed', $err),"\n"; + exit(1); + } +else { + print "$text{'apply_done'}\n"; + exit(0); + } + diff --git a/itsecur-firewall/authdownload.cgi b/itsecur-firewall/authdownload.cgi new file mode 100755 index 000000000..07e35866a --- /dev/null +++ b/itsecur-firewall/authdownload.cgi @@ -0,0 +1,14 @@ +#!/usr/bin/perl +# authdownload.cgi +# Just dump log security file as text + +require './itsecur-lib.pl'; +&can_use_error("logs"); +$log = $config{'authlog'} || &get_authlog_file(); +print "Content-type: text/plain\n\n"; +open(LOG, $log); +while() { + print $_ if (!&is_log_line($_)); + } +close(LOG); + diff --git a/itsecur-firewall/authtail.cgi b/itsecur-firewall/authtail.cgi new file mode 100755 index 000000000..c60fa9e30 --- /dev/null +++ b/itsecur-firewall/authtail.cgi @@ -0,0 +1,26 @@ +#!/usr/bin/perl + +$trust_unknown_referers = 1; +require './itsecur-lib.pl'; +&can_use_error("logs"); +&ReadParse(); +$| = 1; +$SIG{'HUP'} = sub { print "got HUP!\n"; }; +$log = $config{'authlog'} || &get_authlog_file(); +print "Content-type: text/plain\n\n"; + +# Get all the firewall log lines +open(LOG, $log); +while() { + push(@log, $_) if (!&is_log_line($_)); + shift(@log) if (@log > 20); + } + +# Show the last 20, and keep tailing +print @log; +while(1) { + sleep(1); + $line = ; + print $line if ($line && !&is_log_line($line)); + } + diff --git a/itsecur-firewall/backup.cgi b/itsecur-firewall/backup.cgi new file mode 100755 index 000000000..e7d732d12 --- /dev/null +++ b/itsecur-firewall/backup.cgi @@ -0,0 +1,102 @@ +#!/usr/bin/perl +# Actually do a backup + +require './itsecur-lib.pl'; +&can_edit_error("backup"); +&error_setup($text{'backup_err'}); +&ReadParse(); + +# Validate inputs +if ($in{'dest_mode'} == 0) { + $file = &tempname(); + } +elsif ($in{'dest_mode'} == 1) { + $orig_dest = $in{'dest'}; + if (-d $in{'dest'}) { + $in{'dest'} .= "/firewall.zip"; + } + $in{'dest'} =~ /^(.*)\// || &error($text{'backup_edest'}); + -d $1 || &error($text{'backup_edestdir'}); + $file = $in{'dest'}; + $done = &text('backup_done1', $file); + } +elsif ($in{'dest_mode'} == 2) { + gethostbyname($in{'ftphost'}) || &error($text{'backup_eftphost'}); + $in{'ftpfile'} =~ /^\/\S+/ || &error($text{'backup_eftpfile'}); + $in{'ftpuser'} =~ /\S/ || &error($text{'backup_eftpuser'}); + $file = "ftp://$in{'ftpuser'}:$in{'ftppass'}\@$in{'ftphost'}$in{'ftpfile'}"; + $done = &text('backup_done2', $in{'ftphost'}, $in{'ftpfile'}); + } +elsif ($in{'dest_mode'} == 3) { + $in{'email'} =~ /^\S+\@\S+$/ || &error($text{'backup_eemail'}); + $file = "mailto:$in{'email'}"; + $done = &text('backup_done3', $in{'email'}); + } +if (!$in{'pass_def'}) { + $in{'pass'} || &error($text{'backup_epass'}); + } +@what = split(/\0/, $in{'what'}); +@what || &error($text{'backup_ewhat'}); + +if (!$in{'save'}) { + # Create the tar file + $err = &backup_firewall(\@what, $file, $in{'pass_def'} ? undef + : $in{'pass'}); + &error($err) if ($err); + } + +# Save settings +$config{'backup_dest'} = $in{'dest_mode'} == 0 ? undef : $file; +$config{'backup_what'} = join(" ", @what); +$config{'backup_pass'} = $in{'pass_def'} ? undef : $in{'pass'}; +&write_file($module_config_file, \%config); + +if ($in{'save'}) { + # Tell the user about the cron job + &header($text{'backup_title'}, "", + undef, undef, undef, undef, &apply_button()); + print "
\n"; + + print "

",&text('backup_donesched'),"

\n"; + + print "


\n"; + &footer("", $text{'index_return'}); + } +elsif ($in{'dest_mode'} == 0) { + # Send to browser + print "Content-type: application/octet-stream\n\n"; + open(FILE, $file); + while() { + print; + } + close(FILE); + unlink($file); + &remote_webmin_log("backup"); + } +else { + # Tell the user + &header($text{'backup_title'}, "", + undef, undef, undef, undef, &apply_button()); + print "
\n"; + + print "

$done

\n"; + + print "


\n"; + &footer("", $text{'index_return'}); + &remote_webmin_log("backup", undef, $in{'dest'}); + } + +# Setup cron job +$job = &find_backup_job(); +if ($job) { + &cron::delete_cron_job($job); + } +if (!$in{'sched_def'}) { + $job = { 'special' => $in{'sched'}, + 'user' => 'root', + 'command' => $cron_cmd, + 'active' => 1 }; + &cron::create_wrapper($cron_cmd, $module_name, "backup.pl"); + &cron::create_cron_job($job); + } + diff --git a/itsecur-firewall/backup.pl b/itsecur-firewall/backup.pl new file mode 100755 index 000000000..79024263b --- /dev/null +++ b/itsecur-firewall/backup.pl @@ -0,0 +1,16 @@ +#!/usr/bin/perl +# Do a backup on schedule + +$no_acl_check++; +require './itsecur-lib.pl'; + +$file = $config{'backup_dest'}; +if (-d $file) { + $file .= "/firewall.zip"; + } +@what = split(/\s+/, $config{'backup_what'}); +$pass = $config{'backup_pass'}; + +if ($file) { + &backup_firewall(\@what, $file, $pass); + } diff --git a/itsecur-firewall/bootup.cgi b/itsecur-firewall/bootup.cgi new file mode 100755 index 000000000..2540efca2 --- /dev/null +++ b/itsecur-firewall/bootup.cgi @@ -0,0 +1,30 @@ +#!/usr/bin/perl +# bootup.cgi +# Enable or disable iptables at boot time + +require './itsecur-lib.pl'; +&can_edit_error("bootup"); +&ReadParse(); +&foreign_require("init", "init-lib.pl"); +&foreign_require("cron", "cron-lib.pl"); + +# Create the wrapper script +$start_wrapper_script = "$module_config_directory/apply.pl"; +$stop_wrapper_script = "$module_config_directory/stop.pl"; +&cron::create_wrapper($start_wrapper_script, $module_name, "apply.pl"); +&cron::create_wrapper($stop_wrapper_script, $module_name, "stop.pl"); + +if ($in{'boot'}) { + &init::enable_at_boot("itsecur-firewall", + "Start or stop the ITsecur firewall", + $start_wrapper_script, + $stop_wrapper_script); + &remote_webmin_log("bootup"); + } +else { + &init::disable_at_boot("itsecur-firewall"); + &remote_webmin_log("bootdown"); + } + +&redirect(""); + diff --git a/itsecur-firewall/config-*-linux b/itsecur-firewall/config-*-linux new file mode 100644 index 000000000..7697ce1b8 --- /dev/null +++ b/itsecur-firewall/config-*-linux @@ -0,0 +1,9 @@ +type=iptables +what=rules services groups nat pat spoof +fw_any=0 +rusure=1 +show_desc=1 +perpage=40 +add_files=0 +open_log=1 +frags=0 diff --git a/itsecur-firewall/config-freebsd b/itsecur-firewall/config-freebsd new file mode 100644 index 000000000..16374400c --- /dev/null +++ b/itsecur-firewall/config-freebsd @@ -0,0 +1,9 @@ +type=ipf +what=rules services groups nat pat spoof +fw_any=0 +rusure=1 +show_desc=1 +perpage=40 +add_files=0 +open_log=1 +frags=0 diff --git a/itsecur-firewall/config.info b/itsecur-firewall/config.info new file mode 100644 index 000000000..bf3aac6b9 --- /dev/null +++ b/itsecur-firewall/config.info @@ -0,0 +1,15 @@ +line1=Configurable options,11 +fw_any=Include firewall in Any destination?,1,1-Yes,0-No +frags=Block fragmented packets?,1,1-Yes,0-No +auto_dir=Automatic backup directory,3,None +rusure=Ask for confirmation before saving rule?,1,1-Yes,0-No +show_desc=Show rule descriptions?,1,1-Yes,0-No +perpage=Logs to show per page,0,5 +refresh=Seconds between log view refreshes,3,Never +all_files=Include rotated versions of log file,1,1-Yes,0-No +from=From: address for emails,3,Automatic (webmin@hostname) +open_log=Open logs in new window?,1,1-Yes,0-No +line2=System configuration,11 +type=Firewall type,1,iptables-IPtables,ipf-IPF +log=Firewall log file,3,Automatic based on firewall type +authlog=Security log file,3,Automatic based on OS diff --git a/itsecur-firewall/debug_file b/itsecur-firewall/debug_file new file mode 100644 index 000000000..d2ba40b48 --- /dev/null +++ b/itsecur-firewall/debug_file @@ -0,0 +1,5 @@ + OLD @NET23 + NET NET2 + NET ARRAY(0x87a4b98) + NET ARRAY(0x87a09e4) + NET ARRAY(0x8429fc8) diff --git a/itsecur-firewall/defaultacl b/itsecur-firewall/defaultacl new file mode 100644 index 000000000..baf1e48e2 --- /dev/null +++ b/itsecur-firewall/defaultacl @@ -0,0 +1,2 @@ +features=* +rfeatures=* diff --git a/itsecur-firewall/down.cgi b/itsecur-firewall/down.cgi new file mode 100755 index 000000000..a8fd47f7b --- /dev/null +++ b/itsecur-firewall/down.cgi @@ -0,0 +1,16 @@ +#!/usr/bin/perl +# down.cgi +# Move a rule down + +require './itsecur-lib.pl'; +&can_edit_error("rules"); +&ReadParse(); +&lock_itsecur_files(); +@rules = &list_rules(); +($rules[$in{'idx'}], $rules[$in{'idx'}+1]) = + ($rules[$in{'idx'}+1], $rules[$in{'idx'}]); +&save_rules(@rules); +&unlock_itsecur_files(); +&remote_webmin_log("move", "rule", $in{'idx'}+1, $rules[$in{'idx'}]); +&redirect("list_rules.cgi"); + diff --git a/itsecur-firewall/download.cgi b/itsecur-firewall/download.cgi new file mode 100755 index 000000000..fda4b2036 --- /dev/null +++ b/itsecur-firewall/download.cgi @@ -0,0 +1,14 @@ +#!/usr/bin/perl +# download.cgi +# Just dump log file as text + +require './itsecur-lib.pl'; +&can_use_error("logs"); +$log = $config{'log'} || &get_log_file(); +print "Content-type: text/plain\n\n"; +open(LOG, $log); +while() { + print $_ if (&is_log_line($_)); + } +close(LOG); + diff --git a/itsecur-firewall/edit_group.cgi b/itsecur-firewall/edit_group.cgi new file mode 100755 index 000000000..f253675d0 --- /dev/null +++ b/itsecur-firewall/edit_group.cgi @@ -0,0 +1,82 @@ +#!/usr/bin/perl +# edit_group.cgi +# Show a form for editing or creating a group of hosts or nets + +require './itsecur-lib.pl'; +&can_use_error("groups"); +&ReadParse(); +if ($in{'new'}) { + &header($text{'group_title1'}, "", + undef, undef, undef, undef, &apply_button()); + } +else { + &header($text{'group_title2'}, "", + undef, undef, undef, undef, &apply_button()); + @groups = &list_groups(); + if (defined($in{'idx'})) { + $group = $groups[$in{'idx'}]; + } + else { + ($group) = grep { $_->{'name'} eq $in{'name'} } @groups; + $in{'idx'} = $group->{'index'}; + } + } +print "
\n"; + +print "
\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "
$text{'group_header'}
\n"; + +print "\n"; +printf "\n", + $group->{'name'}; + +print "\n"; +print "\n"; + +# Show member groups +print "\n"; +print "\n"; + +print "
$text{'group_name'}
$text{'group_members'}\n"; +$i = 0; +foreach $m (( grep { !/\!?\@/ } @{$group->{'members'}} ), + $blank, $blank, $blank, $blank, $blank, $blank) { + $neg = ($m =~ s/^\!//); + print "\n"; + print " $text{'group_neg'}
\n"; + $i++; + } +print "
\n"; +print " $text{'group_resolv'}\n"; +print "
$text{'group_members2'}\n"; +$i = 0; +foreach $m (( grep { /\!?\@/ } @{$group->{'members'}} ), + $blank, $blank, $blank, $blank, $blank, $blank) { + $neg = ($m =~ s/^\!//); + $m =~ s/^\@//; + print "\n"; + $i++; + } +print "
\n"; + print &group_input("group_$i", $m, 1); + print "
\n"; +if ($in{'new'}) { + print "\n"; + } +else { + print "\n"; + print "\n"; + } +print "
\n"; +&can_edit_disable("groups"); + +print "
\n"; +$from = $in{'from'} || "groups"; +&footer("list_${from}.cgi", $text{$from.'_return'}); + diff --git a/itsecur-firewall/edit_rule.cgi b/itsecur-firewall/edit_rule.cgi new file mode 100755 index 000000000..d541d845a --- /dev/null +++ b/itsecur-firewall/edit_rule.cgi @@ -0,0 +1,192 @@ +#!/usr/bin/perl +# edit_rule.cgi +# Show a form for editing or creating a rule + +require './itsecur-lib.pl'; +&can_use_error("rules"); +&ReadParse(); +@rules = &list_rules(); +if ($in{'new'}) { + &header(defined($in{'insert'}) ? $text{'rule_title3'} + : $text{'rule_title1'}, "", + undef, undef, undef, undef, &apply_button()); + $rule = { 'enabled' => 1, + 'action' => &default_action(), + 'service' => '', + 'source' => '', + 'dest' => '', + 'time' => '*', + 'index' => scalar(@rules) }; + } +else { + &header($text{'rule_title2'}, "", + undef, undef, undef, undef, &apply_button()); + $rule = $rules[$in{'idx'}]; + } +print "
\n"; + +print "
\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "
$text{'rule_header'}
\n"; + +# Show comment +print "\n", + $rule->{'desc'} eq "*" ? "" : $rule->{'desc'}; + +# Show source and destination +foreach $s ('source', 'dest') { + $not = ($rule->{$s} =~ s/^!//g); + $sm = $rule->{$s} eq '*' ? 0 : + $rule->{$s} =~ /^\@/ ? 2 : + $rule->{$s} =~ /^\%/ ? 3 : 1; + + # Any address options + print "\n"; + } + +# Show service +$not = ($rule->{'service'} =~ s/^!//g); +print "\n"; + +# Show action upon match +print "\n"; + +# Show time that this rule applies +$inp = &time_input("time", $rule->{'time'} eq "*" ? undef : $rule->{'time'}); +if ($inp) { + print "\n"; + } +else { + print "\n"; + } + +# Show enabled flag +print "\n"; + +# Show input for position of rule +print "\n"; + +print "
$text{'rule_desc'} \n"; +printf "
",$text{'rule_'.$s}, + " \n"; + print "\n"; + print "\n"; + + # Specific host option + print "\n"; + + # Host group option + local $gv; + if ($rule->{$s} =~ /^\@(.*)$/) { + $gv = $rule->{$s}; + $gv =~ s/(^|\s)@/$1/g; + } + $gi = &group_input("${s}_group", $gv, 0, 1); + if ($gi || $sm == 2) { + print "\n"; + } + + # Interface option + $ii = &iface_input("${s}_iface", + $rule->{$s} =~ /^\%(.*)$/ ? $1 : undef); + if ($ii || $sm == 3) { + print "\n"; + } + + print "
"; + printf " %s\n", + $sm == 0 ? "checked" : "", + $text{'rule_anywhere'}; + print "
"; + printf " %s\n", + $sm == 1 ? "checked" : "", $text{'rule_host'}; + print ""; + printf "\n", + $sm == 1 ? $rule->{$s} : ""; + print "$text{'rule_named'}\n"; + print "
\n"; + print " ", + "$text{'rule_resolv'}\n"; + print "
"; + printf " %s\n", + $sm == 2 ? "checked" : "", $text{'rule_group'}; + print ""; + print $gi; + print "
"; + printf " %s\n", + $sm == 3 ? "checked" : "", $text{'rule_iface'}; + print ""; + print $ii; + print "
\n"; + print "
\n"; + #printf " %s\n", + # $not ? "checked" : "", $text{'rule_not'}; + print "
$text{'rule_service'} \n"; +printf " %s\n", + $rule->{'service'} eq '*' ? "checked" : "", $text{'rule_anyserv'}; +printf " %s
\n", + $rule->{'service'} eq '*' ? "" : "checked", $text{'rule_oneserv'}; +print &service_input("service", + $rule->{'service'} eq '*' ? undef : $rule->{'service'}, + 0, 1); +print "
\n"; +#printf " %s\n", +# $not ? "checked" : "", $text{'rule_not'}; +print "
$text{'rule_action'} \n"; +print &action_input("action", $rule->{'action'}); +print " \n"; +printf " %s\n", + $rule->{'log'} ? 'checked' : '', $text{'rule_log'}; +print "
$text{'rule_time'} "; + printf " %s\n", + $rule->{'time'} eq "*" ? "checked" : "", $text{'rule_anytime'}; + printf " %s\n", + $rule->{'time'} eq "*" ? "" : "checked", $text{'rule_seltime'}; + print $inp; + print "
$text{'rule_enabled'} \n"; +printf " %s\n", + $rule->{'enabled'} ? "checked" : "", $text{'yes'}; +printf " %s\n", + $rule->{'enabled'} ? "" : "checked", $text{'no'}; +print "
$text{'rule_atpos'} \n"; +print "
\n"; +if ($in{'new'}) { + print "\n"; + } +else { + print "\n"; + print "\n"; + } +print "
\n"; +&can_edit_disable("rules"); + +print "
\n"; +&footer("list_rules.cgi", $text{'rules_return'}); + diff --git a/itsecur-firewall/edit_sep.cgi b/itsecur-firewall/edit_sep.cgi new file mode 100755 index 000000000..484b45880 --- /dev/null +++ b/itsecur-firewall/edit_sep.cgi @@ -0,0 +1,74 @@ +#!/usr/bin/perl +# Show a form for editing or creating a rule list section separator + +require './itsecur-lib.pl'; +&can_use_error("rules"); +&ReadParse(); +@rules = &list_rules(); +if ($in{'new'}) { + &header(defined($in{'insert'}) ? $text{'sep_title3'} + : $text{'sep_title1'}, "", + undef, undef, undef, undef, &apply_button()); + $rule = { 'index' => scalar(@rules) }; + } +else { + &header($text{'sep_title2'}, "", + undef, undef, undef, undef, &apply_button()); + $rule = $rules[$in{'idx'}]; + } +print "
\n"; + +print "
\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "
$text{'sep_header'}
\n"; + +# Show separator title +print "\n", + $rule->{'desc'} eq "*" ? "" : $rule->{'desc'}; + +# Show input for position of rule +print "\n"; + +print "
$text{'sep_desc'} \n"; +printf "
$text{'rule_atpos'} \n"; +print "
\n"; +if ($in{'new'}) { + print "\n"; + } +else { + print "\n"; + print "\n"; + } +print "
\n"; +&can_edit_disable("rules"); + +print "
\n"; +&footer("list_rules.cgi", $text{'rules_return'}); + diff --git a/itsecur-firewall/edit_service.cgi b/itsecur-firewall/edit_service.cgi new file mode 100755 index 000000000..aae6e24a4 --- /dev/null +++ b/itsecur-firewall/edit_service.cgi @@ -0,0 +1,73 @@ +#!/usr/bin/perl +# edit_service.cgi +# Show a form for editing or creating a user-defined + +require './itsecur-lib.pl'; +&can_use_error("services"); +&ReadParse(); +if ($in{'new'}) { + &header($text{'service_title1'}, "", + undef, undef, undef, undef, &apply_button()); + } +else { + &header($text{'service_title2'}, "", + undef, undef, undef, undef, &apply_button()); + @services = &list_services(); + #$service = $services[$in{'idx'}]; + if (defined($in{'idx'})) { + $service = $services[$in{'idx'}]; + } + else { + ($service) = grep { $_->{'name'} eq $in{'name'} } @services; + $in{'idx'} = $services->{'index'}; + } + } +print "
\n"; + +print "
\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "
$text{'service_header'}
\n"; + +# Show service name input +print "\n"; +printf "\n", + $service->{'name'}; + +# Show protocols and ports +print "\n"; +print "\n"; + +# Show member services +print "\n"; +print "\n"; + +print "
$text{'service_name'}
$text{'service_ports'}\n"; +print " ", + "\n"; +for($i=0; $i<@{$service->{'protos'}}+6; $i++) { + print "\n"; + print "\n"; + printf "\n", + $i, $service->{'ports'}->[$i]; + print "\n"; + } +print "
$text{'service_proto'}$text{'service_port'}
",&protocol_input( + "proto_$i", $service->{'protos'}->[$i]),"
$text{'service_members'}",&service_input("others", + join(",", @{$service->{'others'}}), 0, 1),"
\n"; +if ($in{'new'}) { + print "\n"; + } +else { + print "\n"; + print "\n"; + } +print "
\n"; +&can_edit_disable("services"); + +print "
\n"; +&footer("list_services.cgi", $text{'services_return'}); + + diff --git a/itsecur-firewall/edit_time.cgi b/itsecur-firewall/edit_time.cgi new file mode 100755 index 000000000..44824d24e --- /dev/null +++ b/itsecur-firewall/edit_time.cgi @@ -0,0 +1,79 @@ +#!/usr/bin/perl +# edit_time.cgi +# Show a form for editing or creating a time range + +require './itsecur-lib.pl'; +&can_use_error("times"); +&ReadParse(); +if ($in{'new'}) { + &header($text{'time_title1'}, "", + undef, undef, undef, undef, &apply_button()); + $time = { 'hours' => '*', + 'days' => '*' }; + } +else { + &header($text{'time_title2'}, "", + undef, undef, undef, undef, &apply_button()); + @times = &list_times(); + if (defined($in{'idx'})) { + $time = $times[$in{'idx'}]; + } + else { + ($time) = grep { $_->{'name'} eq $in{'name'} } @times; + $in{'idx'} = $time->{'index'}; + } + } +print "
\n"; + +print "
\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "
$text{'time_header'}
\n"; + +# Show range name +print "\n"; +printf "\n", + $time->{'name'}; + +# Show hour range +print "\n", + $text{'time_to'}, $to; + +# Show days of week +print "\n"; + +print "
$text{'time_name'}
$text{'time_hours'} \n"; +printf " %s\n", + $time->{'hours'} eq "*" ? "checked" : "", $text{'time_allday'}; +printf "\n", + $time->{'hours'} eq "*" ? "" : "checked"; +($from, $to) = $time->{'hours'} eq "*" ? ( ) : split(/\-/, $time->{'hours'}); +printf "%s \n", + $text{'time_from'}, $from; +printf "%s
$text{'time_days'} \n"; +printf " %s\n", + $time->{'days'} eq "*" ? "checked" : "", $text{'time_allweek'}; +printf " %s
\n", + $time->{'days'} eq "*" ? "" : "checked", $text{'time_sel'}; +%days = map { $_, 1 } split(/,/, $time->{'days'}); +print "
\n"; +if ($in{'new'}) { + print "\n"; + } +else { + print "\n"; + print "\n"; + } +print "
\n"; +&can_edit_disable("times"); + +print "
\n"; +&footer("list_times.cgi", $text{'times_return'}); + diff --git a/itsecur-firewall/edit_user.cgi b/itsecur-firewall/edit_user.cgi new file mode 100755 index 000000000..51f04bb45 --- /dev/null +++ b/itsecur-firewall/edit_user.cgi @@ -0,0 +1,107 @@ +#!/usr/bin/perl +# edit_user.cgi +# Show one Webmin user + +require './itsecur-lib.pl'; +&foreign_require("acl", "acl-lib.pl"); +&can_use_error("users"); +@users = &acl::list_users(); +&ReadParse(); + +if ($in{'new'}) { + &header($text{'user_title1'}, "", + undef, undef, undef, undef, &apply_button()); + %gotmods = ( $module_name, 1 ); + } +else { + &header($text{'user_title2'}, "", + undef, undef, undef, undef, &apply_button()); + ($user) = grep { $_->{'name'} eq $in{'name'} } @users; + %gotmods = map { $_, 1 } @{$user->{'modules'}}; + } +print "
\n"; + +print "
\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "
$text{'user_header'}
\n"; + +# Show username +print "\n", + $user->{'name'}; + +# Show password +print "\n"; + +# Show enabled flag +print "\n", + $user->{'pass'} =~ /^\*LK\*/ ? "checked" : "", $text{'no'}; + +# Show allowed IPS +print "\n"; +print "\n"; + +# Show allowed modules (from list for *this* user) +print "\n"; +&read_acl(\%acl); +@mymods = grep { $acl{$base_remote_user,$_->{'dir'}} } &get_all_module_infos(); +print "\n"; + +# Show access control +print "\n"; +require "./acl_security.pl"; +if ($in{'new'}) { + %uaccess = ( 'features' => 'rules services groups nat pat spoof logs apply', + 'rfeatures' => 'rules services groups nat pat spoof logs apply', + 'edit' => 1 ); + } +else { + %uaccess = &get_module_acl($user->{'name'}); + } +&acl_security_form(\%uaccess); + +print "
$text{'user_name'} \n"; +printf "
$text{'user_pass'} \n"; +if (!$in{'new'}) { + print " ", + "$text{'user_same'}\n"; + print " ", + "$text{'user_change'}\n"; + } +print "
$text{'user_enabled'} \n"; +printf " %s\n", + $user->{'pass'} =~ /^\*LK\*/ ? "" : "checked", $text{'yes'}; +printf " %s
$acl::text{'edit_ips'}\n"; +printf "\n"; +print "
%s
\n", + $user->{'allow'} || $user->{'deny'} ? '' : 'checked', + $acl::text{'edit_all'}; +printf " %s
\n", + $user->{'allow'} ? 'checked' : '', $acl::text{'edit_allow'}; +printf " %s
\n", + $user->{'deny'} ? 'checked' : '', $acl::text{'edit_deny'}; +print "
$text{'user_mods'}

\n"; +if ($in{'new'}) { + print "\n"; + } +else { + print "\n"; + print "\n"; + } +print "
\n"; +&can_edit_disable("users"); + +print "
\n"; +&footer("list_users.cgi", $text{'users_return'}); + diff --git a/itsecur-firewall/enable_rules.cgi b/itsecur-firewall/enable_rules.cgi new file mode 100755 index 000000000..94f24a8b2 --- /dev/null +++ b/itsecur-firewall/enable_rules.cgi @@ -0,0 +1,39 @@ +#!/usr/bin/perl +# Enable, disable, log, un-log or delete a bunch of rules + +require './itsecur-lib.pl'; +&can_edit_error("rules"); +&ReadParse(); +@rules = &list_rules(); +@nums = split(/\0/, $in{'r'}); + +&lock_itsecur_files(); +foreach $n (@nums) { + ($r) = grep { $_->{'index'} == $n } @rules; + if ($in{'enable'}) { + $r->{'enabled'} = 1; + } + elsif ($in{'disable'}) { + $r->{'enabled'} = 0; + } + elsif ($in{'logon'}) { + $r->{'log'} = 1; + } + elsif ($in{'logoff'}) { + $r->{'log'} = 0; + } + elsif ($in{'delete'}) { + @rules = grep { $_ ne $r } @rules; + } + } + +&automatic_backup(); +&save_rules(@rules); +&unlock_itsecur_files(); +&remote_webmin_log($in{'enable'} ? "enable" : + $in{'disable'} ? "disable" : + $in{'logon'} ? "logon" : + $in{'logoff'} ? "logoff" : "delete", "rules", undef, + { 'count' => scalar(@nums) } ); +&redirect("list_rules.cgi"); + diff --git a/itsecur-firewall/images/.xvpics/backup.gif b/itsecur-firewall/images/.xvpics/backup.gif new file mode 100644 index 000000000..e381eb6c3 Binary files /dev/null and b/itsecur-firewall/images/.xvpics/backup.gif differ diff --git a/itsecur-firewall/images/.xvpics/icon.gif b/itsecur-firewall/images/.xvpics/icon.gif new file mode 100644 index 000000000..fa62d28e1 Binary files /dev/null and b/itsecur-firewall/images/.xvpics/icon.gif differ diff --git a/itsecur-firewall/images/.xvpics/restore.gif b/itsecur-firewall/images/.xvpics/restore.gif new file mode 100644 index 000000000..444c546d0 Binary files /dev/null and b/itsecur-firewall/images/.xvpics/restore.gif differ diff --git a/itsecur-firewall/images/.xvpics/syn.gif b/itsecur-firewall/images/.xvpics/syn.gif new file mode 100644 index 000000000..928eb63c6 Binary files /dev/null and b/itsecur-firewall/images/.xvpics/syn.gif differ diff --git a/itsecur-firewall/images/authlogs.gif b/itsecur-firewall/images/authlogs.gif new file mode 100644 index 000000000..7eebfc951 Binary files /dev/null and b/itsecur-firewall/images/authlogs.gif differ diff --git a/itsecur-firewall/images/backup.gif b/itsecur-firewall/images/backup.gif new file mode 100644 index 000000000..20cc7fb14 Binary files /dev/null and b/itsecur-firewall/images/backup.gif differ diff --git a/itsecur-firewall/images/bandwidth.gif b/itsecur-firewall/images/bandwidth.gif new file mode 100755 index 000000000..8ee1b1675 Binary files /dev/null and b/itsecur-firewall/images/bandwidth.gif differ diff --git a/itsecur-firewall/images/down.gif b/itsecur-firewall/images/down.gif new file mode 100644 index 000000000..73025ba51 Binary files /dev/null and b/itsecur-firewall/images/down.gif differ diff --git a/itsecur-firewall/images/gap.gif b/itsecur-firewall/images/gap.gif new file mode 100644 index 000000000..1a7776453 Binary files /dev/null and b/itsecur-firewall/images/gap.gif differ diff --git a/itsecur-firewall/images/groups.gif b/itsecur-firewall/images/groups.gif new file mode 100644 index 000000000..c07cdca69 Binary files /dev/null and b/itsecur-firewall/images/groups.gif differ diff --git a/itsecur-firewall/images/icon.gif b/itsecur-firewall/images/icon.gif new file mode 100644 index 000000000..536685132 Binary files /dev/null and b/itsecur-firewall/images/icon.gif differ diff --git a/itsecur-firewall/images/import.gif b/itsecur-firewall/images/import.gif new file mode 100644 index 000000000..1523c90f1 Binary files /dev/null and b/itsecur-firewall/images/import.gif differ diff --git a/itsecur-firewall/images/lleft.gif b/itsecur-firewall/images/lleft.gif new file mode 100644 index 000000000..bfe181e95 Binary files /dev/null and b/itsecur-firewall/images/lleft.gif differ diff --git a/itsecur-firewall/images/logs.gif b/itsecur-firewall/images/logs.gif new file mode 100644 index 000000000..7eebfc951 Binary files /dev/null and b/itsecur-firewall/images/logs.gif differ diff --git a/itsecur-firewall/images/nat.gif b/itsecur-firewall/images/nat.gif new file mode 100644 index 000000000..b2d7f3b54 Binary files /dev/null and b/itsecur-firewall/images/nat.gif differ diff --git a/itsecur-firewall/images/nat2.gif b/itsecur-firewall/images/nat2.gif new file mode 100644 index 000000000..b2d7f3b54 Binary files /dev/null and b/itsecur-firewall/images/nat2.gif differ diff --git a/itsecur-firewall/images/pat.gif b/itsecur-firewall/images/pat.gif new file mode 100644 index 000000000..84b41c13c Binary files /dev/null and b/itsecur-firewall/images/pat.gif differ diff --git a/itsecur-firewall/images/remote.gif b/itsecur-firewall/images/remote.gif new file mode 100644 index 000000000..22029710d Binary files /dev/null and b/itsecur-firewall/images/remote.gif differ diff --git a/itsecur-firewall/images/report.gif b/itsecur-firewall/images/report.gif new file mode 100644 index 000000000..f723f755b Binary files /dev/null and b/itsecur-firewall/images/report.gif differ diff --git a/itsecur-firewall/images/restore.gif b/itsecur-firewall/images/restore.gif new file mode 100644 index 000000000..35d5f4221 Binary files /dev/null and b/itsecur-firewall/images/restore.gif differ diff --git a/itsecur-firewall/images/rright.gif b/itsecur-firewall/images/rright.gif new file mode 100644 index 000000000..3a96e4eda Binary files /dev/null and b/itsecur-firewall/images/rright.gif differ diff --git a/itsecur-firewall/images/rules.gif b/itsecur-firewall/images/rules.gif new file mode 100644 index 000000000..650a15379 Binary files /dev/null and b/itsecur-firewall/images/rules.gif differ diff --git a/itsecur-firewall/images/services.gif b/itsecur-firewall/images/services.gif new file mode 100644 index 000000000..83f72ea9f Binary files /dev/null and b/itsecur-firewall/images/services.gif differ diff --git a/itsecur-firewall/images/smallicon.gif b/itsecur-firewall/images/smallicon.gif new file mode 100644 index 000000000..ee68f9c87 Binary files /dev/null and b/itsecur-firewall/images/smallicon.gif differ diff --git a/itsecur-firewall/images/spoof.gif b/itsecur-firewall/images/spoof.gif new file mode 100644 index 000000000..a858bade1 Binary files /dev/null and b/itsecur-firewall/images/spoof.gif differ diff --git a/itsecur-firewall/images/syn.gif b/itsecur-firewall/images/syn.gif new file mode 100644 index 000000000..14200fa29 Binary files /dev/null and b/itsecur-firewall/images/syn.gif differ diff --git a/itsecur-firewall/images/times.gif b/itsecur-firewall/images/times.gif new file mode 100644 index 000000000..eff79f606 Binary files /dev/null and b/itsecur-firewall/images/times.gif differ diff --git a/itsecur-firewall/images/top_r1_c1.jpg b/itsecur-firewall/images/top_r1_c1.jpg new file mode 100644 index 000000000..843d390e9 Binary files /dev/null and b/itsecur-firewall/images/top_r1_c1.jpg differ diff --git a/itsecur-firewall/images/up.gif b/itsecur-firewall/images/up.gif new file mode 100644 index 000000000..23ff2fefb Binary files /dev/null and b/itsecur-firewall/images/up.gif differ diff --git a/itsecur-firewall/images/users.gif b/itsecur-firewall/images/users.gif new file mode 100644 index 000000000..1952c6e56 Binary files /dev/null and b/itsecur-firewall/images/users.gif differ diff --git a/itsecur-firewall/import_groups.cgi b/itsecur-firewall/import_groups.cgi new file mode 100755 index 000000000..9e44a3087 --- /dev/null +++ b/itsecur-firewall/import_groups.cgi @@ -0,0 +1,75 @@ +#!/usr/bin/perl +# Actually do an import of host groups + +require './itsecur-lib.pl'; +&can_edit_error("import"); +&error_setup($text{'import_err'}); +&ReadParseMime(); + +# Validate inputs +if (!$in{'src_def'}) { + -r $in{'src'} || &error_cleanup($text{'restore_esrc'}); + $data = `cat $in{'src'}`; + } +else { + $in{'file'} || &error_cleanup($text{'restore_efile'}); + $data = $in{'file'}; + } + +%groups = map { $_->{'name'}, $_ } &list_groups(); + +# Parse the CSV data +$data =~ s/\r//g; +$i = 0; +foreach $line (split(/\n/, $data)) { + # Split into columns + $oldline = $line; + $i++; + next if (!$line); + local @row; + while($line && $line =~ /^,?("([^"]*)"|([^,]*))(.*)$/) { + push(@row, $2 || $3); + $line = $4; + } + @row >= 1 || &error(&text('import_erow', $i, $oldline)); + + # Create a service + $row[0] =~ /\S/ || &error(text('import_egroupname', $i)); + $groups{$row[0]} && &error(text('import_egroupclash', $i, $row[0])); + $group = { 'name' => $row[0] }; + if (@row == 1) { + # Group name is the host name + &valid_host($row[0]) || + &error(text('import_ehost', $i, $row[0])); + $group->{'members'} = [ $row[0] ]; + } + else { + # Hosts are listed + for($i=1; $i<@row; $i++) { + &valid_host($row[$i]) || + &error(text('import_ehost', $i, $row[$i])); + push(@{$group->{'members'}}, $row[$i]); + } + } + push(@newgroups, $group); + } + +# Save the groups +&lock_itsecur_files(); +@groups = &list_groups(); +push(@groups, @newgroups); +&automatic_backup(); +&save_groups(@groups); +&unlock_itsecur_files(); + +# Tell the user +&header($text{'import_title'}, "", + undef, undef, undef, undef, &apply_button()); +print "
\n"; + +print "

",&text('import_done3', scalar(@newgroups)),"

\n"; + +print "


\n"; +&footer("", $text{'index_return'}); +&remote_webmin_log("import", "services", $in{'src_def'} ? undef : $in{'src'}); + diff --git a/itsecur-firewall/import_rules.cgi b/itsecur-firewall/import_rules.cgi new file mode 100755 index 000000000..5f6114fdc --- /dev/null +++ b/itsecur-firewall/import_rules.cgi @@ -0,0 +1,109 @@ +#!/usr/bin/perl +# Actually do an import + +require './itsecur-lib.pl'; +&can_edit_error("import"); +&error_setup($text{'import_err'}); +&ReadParseMime(); + +if (&foreign_check("net")) { + &foreign_require("net", "net-lib.pl"); + foreach $i (&net::active_interfaces(), &net::boot_interfaces()) { + $iface{$i->{'fullname'}} = $i; + } + } +%services = map { $_->{'name'}, $_ } &list_services(); +%times = map { $_->{'name'}, $_ } &list_times(); + +# Validate inputs +if (!$in{'src_def'}) { + -r $in{'src'} || &error_cleanup($text{'restore_esrc'}); + $data = `cat $in{'src'}`; + } +else { + $in{'file'} || &error_cleanup($text{'restore_efile'}); + $data = $in{'file'}; + } + +# Parse the CSV data +$data =~ s/\r//g; +$i = 0; +foreach $line (split(/\n/, $data)) { + # Split into columns + $oldline = $line; + $i++; + next if (!$line); + local @row; + while($line && $line =~ /^,?("([^"]*)"|([^,]*))(.*)$/) { + push(@row, $2 || $3); + $line = $4; + } + @row >= 4 || &error(&text('import_erow', $i, $oldline)); + + # Create a rule + $rule = { 'enabled' => 1 }; + $rule->{'source'} = &parse_srcdest($row[0]); + $rule->{'source'} || &error(text('import_esource', $i, $row[0])); + $rule->{'dest'} = &parse_srcdest($row[1]); + $rule->{'dest'} || &error(text('import_edest', $i, $row[1])); + @servs = split(/\s+/, $row[2]); + foreach $s (@servs) { + $services{$s} || &error(text('import_eservice', $i, $s)); + } + $rule->{'service'} = @servs ? join(",", @servs) : "*"; + if ($row[3] =~ s/\s+log$//i) { + $rule->{'log'} = 1; + } + else { + $rule->{'log'} = 0; + } + &indexof(lc($row[3]), @actions) >= 0 || + &error(text('import_eaction', $i, $row[3])); + $rule->{'action'} = lc($row[3]); + $rule->{'desc'} = $row[4] || "*"; + if ($row[5]) { + $times{$row[5]} || &error(text('import_etime', $i, $row[5])); + $rule->{'time'} = $row[5]; + } + else { + $rule->{'time'} = "*"; + } + push(@newrules, $rule); + } + +# Ensure that new rules are sane + +# Save the rules +&lock_itsecur_files(); +@rules = &list_rules(); +push(@rules, @newrules); +&automatic_backup(); +&save_rules(@rules); +&unlock_itsecur_files(); + +# Tell the user +&header($text{'import_title'}, "", + undef, undef, undef, undef, &apply_button()); +print "
\n"; + +print "

",&text('import_done1', scalar(@newrules)),"

\n"; + +print "


\n"; +&footer("", $text{'index_return'}); +&remote_webmin_log("import", "rules", $in{'src_def'} ? undef : $in{'src'}); + +sub parse_srcdest +{ +if ($_[0] eq "") { + return "*"; + } +elsif (&valid_host($_[0])) { + return $_[0]; + } +elsif ($iface{lc($_[0])}) { + return "%".lc($_[0]); + } +else { + return undef; + } +} diff --git a/itsecur-firewall/import_servs.cgi b/itsecur-firewall/import_servs.cgi new file mode 100755 index 000000000..d9871b8c4 --- /dev/null +++ b/itsecur-firewall/import_servs.cgi @@ -0,0 +1,69 @@ +#!/usr/bin/perl +# Actually do an import of services + +require './itsecur-lib.pl'; +&can_edit_error("import"); +&error_setup($text{'import_err'}); +&ReadParseMime(); + +# Validate inputs +if (!$in{'src_def'}) { + -r $in{'src'} || &error_cleanup($text{'restore_esrc'}); + $data = `cat $in{'src'}`; + } +else { + $in{'file'} || &error_cleanup($text{'restore_efile'}); + $data = $in{'file'}; + } + +%services = map { $_->{'name'}, $_ } &list_services(); + +# Parse the CSV data +$data =~ s/\r//g; +$i = 0; +foreach $line (split(/\n/, $data)) { + # Split into columns + $oldline = $line; + $i++; + next if (!$line); + local @row; + while($line && $line =~ /^,?("([^"]*)"|([^,]*))(.*)$/) { + push(@row, $2 || $3); + $line = $4; + } + @row >= 3 || &error(&text('import_erow', $i, $oldline)); + + # Create a service + $row[0] =~ /\S/ || &error(text('import_eservname', $i)); + $services{$row[0]} && &error(text('import_eservclash', $i, $row[0])); + $serv = { 'name' => $row[0] }; + for($i=1; $i<@row; $i+=2) { + getprotobyname($row[$i]) || + &error(text('import_eproto', $i, $row[$i])); + $row[$i+1] =~ /^\d+$/ || + &error(text('import_eservnum', $i, $row[$i])); + push(@{$serv->{'protos'}}, $row[$i]); + push(@{$serv->{'ports'}}, $row[$i+1]); + } + push(@newservs, $serv); + } + +# Save the services +&lock_itsecur_files(); +@servs = &list_services(); +push(@servs, @newservs); +&automatic_backup(); +&save_services(@servs); +&unlock_itsecur_files(); + +# Tell the user +&header($text{'import_title'}, "", + undef, undef, undef, undef, &apply_button()); +print "
\n"; + +print "

",&text('import_done2', scalar(@newservs)),"

\n"; + +print "


\n"; +&footer("", $text{'index_return'}); +&remote_webmin_log("import", "services", $in{'src_def'} ? undef : $in{'src'}); + diff --git a/itsecur-firewall/import_times.cgi b/itsecur-firewall/import_times.cgi new file mode 100755 index 000000000..571b6e5fa --- /dev/null +++ b/itsecur-firewall/import_times.cgi @@ -0,0 +1,89 @@ +#!/usr/bin/perl +# Actually do an import of time ranges + +require './itsecur-lib.pl'; +&can_edit_error("import"); +&error_setup($text{'import_err'}); +&ReadParseMime(); + +# Validate inputs +if (!$in{'src_def'}) { + -r $in{'src'} || &error_cleanup($text{'restore_esrc'}); + $data = `cat $in{'src'}`; + } +else { + $in{'file'} || &error_cleanup($text{'restore_efile'}); + $data = $in{'file'}; + } + +%times = map { $_->{'name'}, $_ } &list_times(); +%daynum = ( "sun", 0, "mon", 1, "tue", 2, "wed", 3, "thu", 4, "fri", 5, "sat", 6 ); + +# Parse the CSV data +$data =~ s/\r//g; +$i = 0; +foreach $line (split(/\n/, $data)) { + # Split into columns + $oldline = $line; + $i++; + next if (!$line); + local @row; + while($line && $line =~ /^,?("([^"]*)"|([^,]*))(.*)$/) { + push(@row, $2 || $3); + $line = $4; + } + @row >= 1 || &error(&text('import_erow', $i, $oldline)); + + # Create a service + $row[0] =~ /\S/ || &error(text('import_etimename', $i)); + $times{$row[0]} && &error(text('import_etimeclash', $i, $row[0])); + $time = { 'name' => $row[0] }; + if ($row[1]) { + # Week days are given + foreach $d (split(/[\s|,]+/, $row[1])) { + local $dn = $daynum{lc($d)}; + defined($dn) || &error(text('import_etimeday', $i, $d)); + push(@days, $dn); + } + $time->{'days'} = join(",", @days); + } + else { + $time->{'days'} = '*'; + } + if ($row[2]) { + # Time range is given + $row[2] =~ /^(\d+):(\d+)\-(\d+):(\d+)$/ && + $1 >= 0 && $1 < 24 && + $2 >= 0 && $2 < 60 && + $3 >= 0 && $3 < 24 && + $4 >= 0 && $4 < 60 || + &error(&text('import_etimehour', $i, $row[2])); + $time->{'hours'} = $row[2]; + } + else { + $time->{'hours'} = '*'; + } + $time->{'days'} eq '*' && $time->{'hours'} eq '*' && + &error(text('import_etimenone', $i)); + push(@newtimes, $time); + } + +# Save the groups +&lock_itsecur_files(); +@times = &list_times(); +push(@times, @newtimes); +&automatic_backup(); +&save_times(@times); +&unlock_itsecur_files(); + +# Tell the user +&header($text{'import_title'}, "", + undef, undef, undef, undef, &apply_button()); +print "
\n"; + +print "

",&text('import_done4', scalar(@newtimes)),"

\n"; + +print "


\n"; +&footer("", $text{'index_return'}); +&remote_webmin_log("import", "times", $in{'src_def'} ? undef : $in{'src'}); + diff --git a/itsecur-firewall/index.cgi b/itsecur-firewall/index.cgi new file mode 100755 index 000000000..328d18715 --- /dev/null +++ b/itsecur-firewall/index.cgi @@ -0,0 +1,78 @@ +#!/usr/bin/perl +# index.cgi +# Show icons for rules, services, groups and NAT + +require './itsecur-lib.pl'; +&header($text{'index_title'}, "", undef, 1, 1, 0, &apply_button(), undef, undef, + &text('index_version', $module_info{'version'})); +print "
\n"; + +# Icons table +@can_opts = grep { $_ eq "backup" || $_ eq "restore" || $_ eq "remote" || $_ eq "import" ? &can_edit($_) : &can_use($_) } @opts; +@links = map { "list_".$_.".cgi" } @can_opts; +@titles = map { $text{$_."_title"} } @can_opts; +@icons = map { "images/".$_.".gif" } @can_opts; +@hrefs = map { ($_ eq "logs" || $_ eq "authlogs") && $config{'open_logs'} ? "target=_new" : "" } @can_opts; +&itsecur_icons_table(\@links, \@titles, \@icons, 4, \@hrefs); + +if (&can_edit("apply") || &can_edit("bootup")) { + print "
\n"; + } + +print "\n"; + +if (&can_edit("apply")) { + # Apply button + print "\n"; + print "\n"; + print "\n"; + print "\n"; + } + +if (&can_edit("bootup")) { + &foreign_require("init", "init-lib.pl"); + $atboot = &init::action_status("itsecur-firewall") == 2; + + # At-boot button + print "\n"; + print "\n"; + print "\n"; + } + +print "
$text{'index_applydesc'}
\n"; + printf " %s\n", + $atboot ? "checked" : "", $text{'yes'}; + printf " %s\n", + $atboot ? "" : "checked", $text{'no'}; + print " $text{'index_bootupdesc'}
\n"; + +print "
\n"; +&footer("/", $text{'index'}); + +# itsecur_icons_table(&links, &titles, &icons, [columns], [href], [width], [height]) +# Renders a 4-column table of icons +sub itsecur_icons_table +{ +&load_theme_library(); +if (defined(&theme_icons_table)) { + &theme_icons_table(@_); + return; + } +local ($i, $need_tr); +local $cols = $_[3] ? $_[3] : 4; +local $per = int(100.0 / $cols); +print "\n"; +for($i=0; $i<@{$_[0]}; $i++) { + if ($i%$cols == 0) { print "\n"; } + print "\n"; + if ($i%$cols == $cols-1) { print "\n"; } + } +while($i++%$cols) { print "\n"; $need_tr++; } +print "\n" if ($need_tr); +print "
\n"; + &generate_icon($_[2]->[$i], $_[1]->[$i], $_[0]->[$i], + ref($_[4]) ? $_[4]->[$i] : $_[4], $_[5], $_[6]); + print "
\n"; +} + + diff --git a/itsecur-firewall/ipf-lib.pl b/itsecur-firewall/ipf-lib.pl new file mode 100644 index 000000000..7721f96a7 --- /dev/null +++ b/itsecur-firewall/ipf-lib.pl @@ -0,0 +1,348 @@ +# ipf-lib.pl +# Defines firewall functions for IPF + +@actions = ( "allow", "deny", "reject" ); +$script_file = "$module_config_directory/ipf.sh"; +$nat_conf = "$module_config_directory/nat.conf"; +use Time::Local; + +# apply_rules(&rules, &hosts, &services) +# Turns the firewall configuration into an IPF script +sub apply_rules +{ +&deactivate_all_interfaces(); # will add those needed later +local $ipfw = &has_command("ipfw"); + +# Open scripts +open(SCRIPT, ">$script_file"); +print SCRIPT "#!/bin/sh\n"; +open(NATCONF, ">$nat_conf"); + +# Clear existing rules +print SCRIPT "$ipfw -f flush\n"; + +# Add rules for spoofing +local ($spoofiface, @nets) = &get_spoof(); +local $num = 1; +if ($spoofiface) { + local $n; + foreach $n (@nets) { + print_ipfw("drop ip from $n to any recv $spoofiface"); + } + } + +# Allow established connections +$num = 2; +print_ipfw("allow tcp from any to any established"); + +# Always allow localhost +$num = 3; +print_ipfw("allow ip from any to any recv lo"); + +if ($config{'frags'}) { + # Drop fragments + # XXX how? + } + +# Add primary rules +local $r; +local @rules = &list_rules(); +local %services = map { $_->{'name'}, $_ } &list_services(); +local @groups = &list_groups(); +foreach $r (@rules) { + next if (!$r->{'enabled'}); + next if ($r->{'sep'}); + $num = $r->{'num'}*10; + + # Work out all source and destination hosts? + local @sources = &expand_hosts($r->{'source'}, \@groups); + local @dests = &expand_hosts($r->{'dest'}, \@groups); + + # Need to output a rule for every possible combination + local ($source, $dest); + local $aarg = $r->{'action'}; + local $logarg = $r->{'log'} ? "log" : ""; + foreach $source (@sources) { + $source =~ s/^!(\S.*)$/not $1/; + local $sarg = $source eq '*' ? "from any" : + $source =~ /^%(.*)$/ ? "from any" : + "from $source"; + local $siarg = $source =~ /^%(.*)$/ ? "xmit $1" : ""; + + foreach $dest (@dests) { + $dest =~ s/^!(\S.*)$/! $1/; + local $darg = $dest eq '*' && !$config{'fw_any'} && + $r->{'action'} eq 'allow' ? "! -d me" : + $dest =~ /^%(.*)$/ ? "to any" : + "to $dest"; + local $diarg = $dest =~ /^%(.*)$/ ? "recv $1" : ""; + + if ($r->{'service'} ne '*') { + # Output one rule for each service + local ($protos, $ports) = + &combine_services($r->{'service'}, + \%services); + for($i=0; $i<@$protos; $i++) { + local $pr = lc($protos->[$i]); + local $pt = $ports->[$i]; + + local $parg; + local $opts; + local $prarg; + if ($pr eq "gre") { + # handle old GRE protocols + $pr = "ip"; + $pr = "gre"; + } + if ($pr eq "ip") { + $prarg = $pt; + } + else { + $prarg = $pr; + } + if ($pr eq "ip") { + # No port for IP + } + elsif ($pt =~ /^(\d+)$/ || $pt eq '*') { + if ($pr eq 'icmp') { + $opts = " icmptype $pt" if ($pt ne '*'); + } + else { + $parg = $pt; + } + } + elsif ($pt =~ /^(\d+)\-(\d+)$/) { + $parg = "$1-$2"; + } + else { + $parg = join(",", split(/\s+/, $pt)); + } + print_ipfw("$aarg $logarg $prarg $sarg $darg $parg $opts $siarg $diarg"); + } + } + else { + # Single service-independent rule + print_ipfw("$aarg $logarg ip $sarg $darg $siarg $diarg"); + } + } + } + } + +# Add syn flood and spoofing rules +local ($flood, $spoof, $fin) = &get_syn(); +if ($flood) { + # Configure kernel to use syn cookies + print SCRIPT "sysctl net.inet.tcp.syncookies=1\n"; + } +else { + # Configure kernel to disable syn cookies + print SCRIPT "sysctl net.inet.tcp.syncookies=0\n"; + } +if ($spoof) { + # Drop TCP connection starts without SYN set + $num = 60000; + print_ipfw("allow tcp from any to any established"); + print_ipfw("deny tcp from any to any tcpflags !syn"); + } +if ($fin) { + # Drop TCP packets with both SYN and FIN set + $num = 61000; + print_ipfw("deny tcp from any to any tcpflags syn,fin"); + } + +local ($natiface, @nets) = &get_nat(); +local @maps; +if ($natiface) { + # Add rules for NAT + @maps = grep { ref($_) } @nets; + @nets = grep { !ref($_) } @nets; + local $m; + foreach $m (@maps) { + # Add rule for static NAT (internal to external host mapping) + print NATCONF "map $natiface $m->[1]/32 -> $m->[0]/32\n"; + print NATCONF "map $natiface $m->[0]/32 -> $m->[1]/32\n"; + if ($m->[2]) { + &activate_interface($m->[2], $m->[0]); + } + } + local $n; + foreach $n (@nets) { + # Add rule for dynamic NAT + local @sources = &expand_hosts("\@$n", \@groups); + local $source; + foreach $source (@sources) { + $source =~ s/^!(\S.*)$/! $1/; + print NATCONF "map $natiface $source -> 0/32\n"; + } + } + } + +# Add rules for PAT (external port to internal host mapping) +local @forwards = &get_pat(); +local $f; +foreach $f (@forwards) { + next if (!$f->{'iface'}); + local ($protos, $ports) = &combine_services($f->{'service'}, + \%services); + local $i; + for($i=0; $i<@$protos; $i++) { + local $pr = lc($protos->[$i]); + local $pt = $ports->[$i]; + next if ($pr ne 'tcp' && $pr ne 'udp'); + print NATCONF "rdr $f->{'iface'} 0/32 port $pt -> $f->{'host'} port $pt $pr\n"; + } + } + +# Allow all by default +$num = 60001; +print_ipfw("allow ip from any to any"); +close(SCRIPT); +chmod(0755, $script_file); +close(NATCONF); + +# Run the script +#return "
".`cat $script_file`."
\n"; +local $out = `cd /; $script_file 2>&1 $out"; + } + +# Run the NAT config +$out = `cd /; ipnat -C >/dev/null ; ipnat -f $nat_conf 2>&1 $out"; + } + +return undef; +} + +sub print_ipfw +{ +print SCRIPT "$ipfw add $num $_[0]\n"; +} + +# stop_rules() +# Allow all traffic +sub stop_rules +{ +&deactivate_all_interfaces(); +system("cd /; ipfw -f flush; ipfw add allow ip from any to any"); +system("cd /; ipnat -C"); +} + +# enable_routing() +# Enable routing under BSD +sub enable_routing +{ +system("sysctl net.inet.ip.forwarding=1 >/dev/null 2>&1"); +} + +# disable_routing() +# Disable routing under BSD +sub disable_routing +{ +system("sysctl net.inet.ip.forwarding=0 >/dev/null 2>&1"); +} + +sub get_log_file +{ +return "/var/log/security"; +} + +sub get_authlog_file +{ +return "/var/log/security"; +} + +sub is_log_line +{ +return $_[0] =~ /\sipfw:\s/; +} + +$time_now = time(); +@time_now = localtime($time_now); +%mmap = ( 'jan' => 0, 'feb' => 1, 'mar' => 2, 'apr' => 3, + 'may' => 4, 'jun' => 5, 'jul' => 6, 'aug' => 7, + 'sep' => 8, 'oct' => 9, 'nov' =>10, 'dec' =>11 ); + +# parse_log_line(line) +# Parses a line into a log info structure, or returns undef +sub parse_log_line +{ +if (&is_log_line($_[0])) { + local $info = { }; + if ($_[0] =~ /^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)/) { + local $tm = timelocal($5, $4, $3, $2, $mmap{lc($1)}, $time_now[5]); + if ($tm > $time_now + 24*60*60) { + # Was really last year + $tm = timelocal($5, $4, $3, $2, $mmap{lc($1)}, $time_now[5]-1); + } + $info->{'time'} = $tm; + } + if ($_[0] =~ /ipfw:\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(in|out)\s+\S+\s+(\S+)/) { + if ($1 >= 10 && $1 < 60000) { + $info->{'rule'} = int($1/10); + } + $info->{'action'} = lc($2); + $info->{'action'} = "allow" if ($info->{'action'} eq "accept"); + $info->{'proto'} = uc($3); + if ($6 eq "in") { + $info->{'dst_iface'} = $7; + } + else { + $info->{'src_iface'} = $7; + } + local ($src, $dst) = ($4, $5); + if ($src =~ /^(\S+):(\d+)$/) { + $info->{'src'} = $1; + $info->{'src_port'} = $2; + } + else { + $info->{'src'} = $src; + } + if ($dst =~ /^(\S+):(\d+)$/) { + $info->{'dst'} = $1; + $info->{'dst_port'} = $2; + } + else { + $info->{'dst'} = $dst; + } + if ($info->{'proto'} =~ /^(ICMP):(\d+)/) { + $info->{'proto'} = $1; + $info->{'dst_port'} = $2; + } + } + return $info; + } +else { + return undef; + } +} + +sub allow_action +{ +return $_[0]->{'action'} eq 'allow'; +} + +sub deny_action +{ +return $_[0]->{'action'} eq 'deny'; +} + +sub default_action +{ +return "deny"; +} + +sub supports_time +{ +return 0; +} + +sub supports_bandwidth +{ +return 0; +} + +1; + diff --git a/itsecur-firewall/iptables-lib.pl b/itsecur-firewall/iptables-lib.pl new file mode 100644 index 000000000..4376cb782 --- /dev/null +++ b/itsecur-firewall/iptables-lib.pl @@ -0,0 +1,496 @@ +# iptables-lib.pl +# Defines firewall functions for IPtables + +@actions = ( 'accept', 'drop', 'reject', 'ignore' ); +$save_file = "$module_config_directory/iptables.save"; +$prerules = "$module_config_directory/prerules"; +$postrules = "$module_config_directory/postrules"; +$prenat = "$module_config_directory/prenat"; +$postnat = "$module_config_directory/postnat"; +$premangle = "$module_config_directory/premangle"; +$postmangle = "$module_config_directory/postmangle"; + + +use Time::Local; + +# apply_rules() +# Turns the firewall configuration into an IPtables save file, and then +# applies it. +sub apply_rules +{ +&deactivate_all_interfaces(); # will add those needed later + +local @dayname = ( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ); + +# Create the groups +open(SAVE, ">$save_file"); +print SAVE "*filter\n"; +print SAVE ":INPUT ACCEPT [0:0]\n"; +print SAVE ":OUTPUT ACCEPT [0:0]\n"; +print SAVE ":FORWARD ACCEPT [0:0]\n"; +print SAVE ":SYN-FLOOD -\n"; + +# Disable bandwith monitor +# Have a lots of issues. +# AA 2006-02-21 + + +#if ($config{'bandwidth'}) { +# # Add rules for bandwidth logging +# print SAVE "-A INPUT -i $config{'bandwidth'} -j LOG --log-prefix BANDWIDTH_IN: --log-level debug\n"; +# print SAVE "-A FORWARD -i $config{'bandwidth'} -j LOG --log-prefix BANDWIDTH_IN: --log-level debug\n"; +# print SAVE "-A FORWARD -o $config{'bandwidth'} -j LOG --log-prefix BANDWIDTH_OUT: --log-level debug\n"; +# print SAVE "-A OUTPUT -o $config{'bandwidth'} -j LOG --log-prefix BANDWIDTH_OUT: --log-level debug\n"; +# } + +# Add rules for spoofing +local ($spoofiface, @nets) = &get_spoof(); +if ($spoofiface) { + local $n; + foreach $n (@nets) { + print SAVE "-A INPUT -i $spoofiface -s $n -j DROP\n"; + } + } + +# Always allow established connections +print SAVE "-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT\n"; +print SAVE "-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT\n"; + +# Always allow localhost +print SAVE "-A INPUT -i lo -j ACCEPT\n"; +print SAVE "-A OUTPUT -o lo -j ACCEPT\n"; + +if ($config{'frags'}) { + # Drop fragments + print SAVE "-A INPUT -p ip -f -j DROP\n"; + print SAVE "-A OUTPUT -p ip -f -j DROP\n"; + print SAVE "-A FORWARD -p ip -f -j DROP\n"; + } + +# Add syn flood and spoofing rules +local ($flood, $spoof, $fin) = &get_syn(); +if ($flood) { + # Limit number of syns / second + print SAVE "-A SYN-FLOOD -m limit --limit 1/s --limit-burst 4 -j RETURN\n"; + print SAVE "-A SYN-FLOOD -j DROP\n"; + print SAVE "-A INPUT -p tcp -m tcp --syn -j SYN-FLOOD\n"; + } +if ($spoof) { + # Drop TCP connection starts without SYN set + print SAVE "-A INPUT -p tcp -m tcp ! --syn -m state --state NEW -j DROP\n"; + } +if ($fin) { + # Drop TCP packets with both SYN and FIN + print SAVE "-A INPUT -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -j DROP\n"; + } + +# Load PRErules +open(STATICS, $prerules); +while() { + print SAVE "$_"; + } +close(STATICS); + +# Add primary rules +local $r; +local @rules = &list_rules(); +local %services = map { $_->{'name'}, $_ } &list_services(); +local %times = map { $_->{'name'}, $_ } &list_times(); +local @groups = &list_groups(); +foreach $r (@rules) { + next if (!$r->{'enabled'}); + next if ($r->{'sep'}); + + # Work out all source and destination hosts? + local @sources = &expand_hosts($r->{'source'}, \@groups); + local @dests = &expand_hosts($r->{'dest'}, \@groups); + + # Work out time args + local $timearg; + if ($r->{'time'} ne "*") { + local $time = $times{$r->{'time'}}; + $timearg .= "-m time"; + if ($time->{'hours'} ne "*") { + local ($from, $to) = split(/\-/, $time->{'hours'}); + $timearg .= " --timestart $from --timestop $to"; + } + if ($time->{'days'} ne "*") { + $timearg .= " --days ". + join(",", map { $dayname[$_] } + split(/,/, $time->{'days'})); + } + } + + # Need to output a rule for every possible combination + local ($source, $dest); + local $aarg = "-j ".uc($r->{'action'}); + local $n = $r->{'num'}; + local $logpfx = "--log-prefix RULE_${n}:".uc($r->{'action'}).":"; + foreach $source (@sources) { + $source =~ s/^!(\S.*)$/! $1/; + local $sarg = $source eq '*' ? "" : + $source =~ /^%(.*)$/ ? "-o $1" : + "-s $source"; + local $me = &my_address_in($source); + + foreach $dest (@dests) { + $dest =~ s/^!(\S.*)$/! $1/; + local $darg = $dest eq '*' && !$config{'fw_any'} && + $r->{'action'} eq 'accept' ? "! -d $me" : + $dest eq '*' ? "" : + $dest =~ /^%(.*)$/ ? "-i $1" : + "-d $dest"; + + if ($r->{'service'} ne '*') { + # Output one rule for each real service + local ($protos, $ports) = + &combine_services($r->{'service'}, + \%services); + for($i=0; $i<@$protos; $i++) { + local $pr = lc($protos->[$i]); + local $pt = $ports->[$i]; + local $marg = $pr eq 'tcp' || + $pr eq 'udp' || $pr eq 'icmp' ? "-m $pr" : ""; + local $prarg; + if ($pr eq "gre") { + # handle old GRE protocols + $pr = "ip"; + $pr = "gre"; + } + if ($pr eq "ip") { + $prarg = "-p $pt"; + } + else { + $prarg = "-p $pr"; + } + local $parg; + if ($pr eq "ip") { + # No need for port number + } + elsif ($pt =~ /^(\d+)$/ || $pt eq '*') { + if ($pr eq 'icmp') { + $parg = "--icmp-type $pt" if ($pt ne '*'); + } + else { + $parg = "--destination-port $pt"; + } + } + elsif ($pt =~ /^(\d+)\-(\d+)$/) { + $parg = "--dport $1:$2"; + } + else { + $parg = "--dports ". + join(",", split(/\s+/, $pt)); + $marg .= " -m multiport"; + } + if ($r->{'log'}) { + if ($source !~ /^%(.*)$/) { + #if ($dest !~ /^%(.*)$/) { + print SAVE "-A INPUT $marg $prarg $timearg $sarg $darg $parg -j LOG $logpfx\n"; + } + print SAVE "-A FORWARD $marg $prarg $timearg $sarg $darg $parg -j LOG $logpfx\n"; + } + if ($source !~ /^%(.*)$/) { + #if ($dest !~ /^%(.*)$/) { + print SAVE "-A INPUT $marg $prarg $timearg $sarg $darg $parg $aarg\n"; + } + print SAVE "-A FORWARD $marg $prarg $timearg $sarg $darg $parg $aarg\n"; + } + } + else { + # Single service-independent rule + if ($r->{'log'}) { + if ($source !~ /^%(.*)$/) { + #if ($dest !~ /^%(.*)$/) { + print SAVE "-A INPUT $timearg $sarg $darg -j LOG $logpfx\n"; + } + print SAVE "-A FORWARD $timearg $sarg $darg -j LOG $logpfx\n"; + } + if ($source !~ /^%(.*)$/) { + #if ($dest !~ /^%(.*)$/) { + print SAVE "-A INPUT $timearg $sarg $darg $aarg\n"; + } + print SAVE "-A FORWARD $timearg $sarg $darg $aarg\n"; + } + } + } + } +# Load POSTrules +open(STATICS, $postrules); +while() { + print SAVE "$_"; + } +close(STATICS); + + +print SAVE "COMMIT\n"; + +print SAVE "*nat\n"; +print SAVE ":PREROUTING ACCEPT [0:0]\n"; +print SAVE ":POSTROUTING ACCEPT [0:0]\n"; +print SAVE ":OUTPUT ACCEPT [0:0]\n"; + + + +local ($natiface, @nets) = &get_nat(); +local @maps; +if ($natiface) { + # Add rules for NAT + @maps = grep { ref($_) } @nets; + @nets = grep { !ref($_) } @nets; + + # Add rules for NAT exclusions + local ($e,$my_e); + foreach $e (grep { $_ =~ /^\!/ } @nets) { + $my_e = $e; + $my_e =~ s/^\!//; + local @dests = &expand_hosts("\@$my_e", \@groups); + local $dest; + + foreach $dest (@dests) { + $dest =~ s/^!(\S.*)$/! $1/; + #print SAVE "-A POSTROUTING -o $natiface -d $dest -j RETURN\n"; + #print SAVE "-A PREROUTING -i $natiface -d $dest -j RETURN\n"; + print SAVE "-A POSTROUTING -d $dest -j RETURN\n"; + print SAVE "-A PREROUTING -d $dest -j RETURN\n"; + } + } + #Clear the nets_copy + + # Load PREnat After Return + open(STATICS, $prenat); + while() { + print SAVE "$_"; + } + close(STATICS); + + + # Add rules for static NAT + local $m; + local ($intf_i,$intf_o,$option_i,$option_o); + + # local @dests = &expand_hosts("\@$my_e", \@groups); + local (@tmp,$internal,$external); + + + foreach $m (@maps) { + @tmp = &expand_hosts("\@$m->[1]", \@groups); + $internal=$tmp[0]; + #@tmp = &expand_hosts("\@$m->[0]", \@groups); + $external="$m->[0]"; + if ($m->[2]) { + $intf_i= " -i $m->[2] "; + $intf_o= " -o $m->[2] "; + } else { + $intf_i= ""; + $intf_o= ""; + } + if (&check_netaddress($external)) { + $option_i=" -j NETMAP "; + $option_o=" -j NETMAP "; + } elsif (&check_netaddress($internal)) { + $option_o=" -j SNAT "; + if ($m->[2]) { + &activate_interface($m->[2], $external); + } + } else { + $option_i=" -j DNAT "; + $option_o=" -j SNAT "; + if ($m->[2]) { + &activate_interface($m->[2], $external); + } + } + (! &check_netaddress($internal) ) && print SAVE "-A PREROUTING $intf_i -d $external $option_i --to $internal\n"; + print SAVE "-A POSTROUTING $intf_o -s $internal $option_o --to $external\n"; + } + + # Load POSTnat + open(STATICS, $postnat); + while() { + print SAVE "$_"; + } + close(STATICS); + + # Add rules for dynamic NAT + + local $n; + foreach $n (grep { $_ !~ /^\!/ } @nets) { + local @sources = &expand_hosts("\@$n", \@groups); + local $source; + foreach $source (@sources) { + $source =~ s/^!(\S.*)$/! $1/; + print SAVE "-A POSTROUTING -o $natiface -s $source -j MASQUERADE\n"; + } + } + } + +# Add rules for PAT +local @forwards = &get_pat(); +local $f; +foreach $f (@forwards) { + next if (!$f->{'iface'}); + local ($protos, $ports) = &combine_services($f->{'service'}, + \%services); + local $i; + for($i=0; $i<@$protos; $i++) { + local $pr = lc($protos->[$i]); + local $pt = $ports->[$i]; + next if ($pr ne 'tcp' && $pr ne 'udp'); + print SAVE "-A PREROUTING -m $pr -p $pr --dport $pt -i $f->{'iface'} -j DNAT --to-destination $f->{'host'}:$pt\n"; + } + } + +print SAVE "COMMIT\n"; + +print SAVE "*mangle\n"; +print SAVE ":PREROUTING ACCEPT [0:0]\n"; +print SAVE ":OUTPUT ACCEPT [0:0]\n"; +# Load PREmangle +open(STATICS, $premangle); +while() { + print SAVE "$_"; + } +close(STATICS); +# Add rules + +# Load POSTmangle +open(STATICS, $postmangle); +while() { + print SAVE "$_"; + } +close(STATICS); +print SAVE "COMMIT\n"; +close(SAVE); + +# Apply the save file +local $out = `iptables-restore <$save_file 2>&1`; +if ($?) { + return "iptables-restore output :
$out
"; + } +return undef; +} + +# stop_rules() +# Cancel all firewall rules and return to the default settings (allow all) +sub stop_rules +{ +&deactivate_all_interfaces(); +local $table; +foreach $table ([ "filter", "INPUT", "OUTPUT", "FORWARD" ], + [ "nat", "PREROUTING", "POSTROUTING", "OUTPUT" ], + [ "mangle", "PREROUTING", "OUTPUT" ]) { + local ($name, @chains) = @$table; + local $cmd; + foreach $cmd ((map { "iptables -t $name -P $_ ACCEPT" } @chains), + "iptables -t $name -F", + "iptables -t $name -X", + "iptables -t $name -Z") { + local $out = `$cmd 2>&1`; + if ($?) { + return "$cmd output : $out"; + } + } + } +return undef; +} + +# enable_routing() +# Enable routing under Linux +sub enable_routing +{ +system("sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1"); +} + +# disable_routing() +# Disable routing under Linux +sub disable_routing +{ +system("sysctl -w net.ipv4.ip_forward=0 >/dev/null 2>&1"); +} + +sub get_log_file +{ +return "/var/log/messages"; +} + +sub get_authlog_file +{ +return -r "/var/log/secure" ? "/var/log/secure" : + -r "/var/log/security" ? "/var/log/security" : + -r "/var/log/authlog" ? "/var/log/authlog" : + "/var/log/auth"; +} + +sub is_log_line +{ +return $_[0] =~ /IN=.*OUT=/; +} + +$time_now = time(); +@time_now = localtime($time_now); +%mmap = ( 'jan' => 0, 'feb' => 1, 'mar' => 2, 'apr' => 3, + 'may' => 4, 'jun' => 5, 'jul' => 6, 'aug' => 7, + 'sep' => 8, 'oct' => 9, 'nov' =>10, 'dec' =>11 ); + +# parse_log_line(line) +# Parses a line into a log info structure, or returns undef +sub parse_log_line +{ +if (&is_log_line($_[0])) { + local $info = { }; + if ($_[0] =~ /RULE_(\d+):([^\s:]+)/) { + $info->{'rule'} = $1; + $info->{'action'} = lc($2); + } + if ($_[0] =~ /^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)/) { + local $tm = timelocal($5, $4, $3, $2, $mmap{lc($1)}, $time_now[5]); + if ($tm > $time_now + 24*60*60) { + # Was really last year + $tm = timelocal($5, $4, $3, $2, $mmap{lc($1)}, $time_now[5]-1); + } + $info->{'time'} = $tm; + } + $info->{'src_iface'} = $1 if ($_[0] =~ /OUT=(\S*)/); + $info->{'dst_iface'} = $1 if ($_[0] =~ /IN=(\S*)/); + $info->{'src'} = $1 if ($_[0] =~ /SRC=(\S*)/); + $info->{'dst'} = $1 if ($_[0] =~ /DST=(\S*)/); + $info->{'size'} = $1 if ($_[0] =~ /LEN=(\S*)/); + $info->{'proto'} = $1 if ($_[0] =~ /PROTO=(\S*)/); + $info->{'src_port'} = $1 if ($_[0] =~ /SPT=(\S*)/); + $info->{'dst_port'} = $1 if ($_[0] =~ /DPT=(\S*)/); + $info->{'dst_port'} = $1 if ($_[0] =~ /TYPE=(\S*)/ && + $info->{'proto'} eq 'ICMP'); + return $info; + } +else { + return undef; + } +} + +sub allow_action +{ +return $_[0]->{'action'} eq 'accept'; +} + +sub deny_action +{ +return $_[0]->{'action'} eq 'drop'; +} + +sub default_action +{ +return "drop"; +} + +sub supports_time +{ +return 1; +} + +sub supports_bandwidth +{ +return &foreign_check("bandwidth"); +} + +1; + diff --git a/itsecur-firewall/itsecur-lib.pl b/itsecur-firewall/itsecur-lib.pl new file mode 100644 index 000000000..6eb506b58 --- /dev/null +++ b/itsecur-firewall/itsecur-lib.pl @@ -0,0 +1,1510 @@ +# itsecure-lib.pl +# Version +# ITsecur +# Common functions for all firewall types +# XXX only backup firewall module users? + +BEGIN { push(@INC, ".."); }; +use WebminCore; +&init_config(); +do "$config{'type'}-lib.pl"; + +@opts = ( 'rules', 'services', 'groups', 'nat','nat2', 'pat', 'spoof', 'syn', 'logs', + 'authlogs', 'report', + 'users', + &supports_time() ? ('times') : (), + 'backup', 'restore', + 'remote', 'import' ); +# Take out to test +# &supports_bandwidth() ? ('bandwidth') : (), +@backup_opts = grep { $_ ne 'logs' && $_ ne 'backup' && $_ ne 'restore' } + (@opts, 'ipsec', 'searches', 'config'); + +$groups_file = "$module_config_directory/groups"; +$standard_services_file = "$module_root_directory/standard-services"; +$services_file = "$module_config_directory/services"; +$rules_file = "$module_config_directory/rules"; +$nat_file = "$module_config_directory/nat"; +$nat2_file = "$module_config_directory/nat2"; +$pat_file = "$module_config_directory/pat"; +$spoof_file = "$module_config_directory/spoof"; +$syn_file = "$module_config_directory/syn"; +$times_file = "$module_config_directory/times"; +$active_interfaces = "$module_config_directory/active"; +$prerules = "$module_config_directory/prerules"; +$postrules = "$module_config_directory/postrules"; +$prenat = "$module_config_directory/prenat"; +$postnat = "$module_config_directory/postnat"; +$debug_file = "$module_config_directory/debug"; + +$searches_dir = "$module_config_directory/searches"; + +@config_files = ( $groups_file, $services_file, + $rules_file, $nat_file, $nat2_file, $pat_file, $spoof_file, + $syn_file, $times_file ); + +%access = &get_module_acl(); +if (defined($access{'edit'})) { + if ($access{'edit'}) { + @edit_access = @read_access = split(/\s+/, $access{'features'}); + } + else { + @read_access = split(/\s+/, $access{'features'}); + } + } +else { + @edit_access = split(/\s+/, $access{'features'}); + @read_access = split(/\s+/, $access{'rfeatures'}); + } +%edit_access = map { $_, 1 } @edit_access; +%read_access = map { $_, 1 } @read_access; + +$cron_cmd = "$module_config_directory/backup.pl"; + +# list_groups([file]) +# Returns a list of groups. Each has a name and zero or more member hosts, +# IP addresses, networks or other groups. +sub list_groups +{ +local @rv; +open(GROUPS, $_[0] || $groups_file); +while() { + s/\r|\n//g; + if (/^(\S+)\t+(.*)$/) { + local $group = { 'name' => $1, + 'members' => [ split(/\t+/, $2) ], + 'index' => scalar(@rv) }; + push(@rv, $group); + } + } +close(GROUPS); +return @rv; +} + +# save_groups(group, ...) +# Updates the groups list +sub save_groups +{ +local $g; +local @SortGroups=(); +foreach $g (@_) { + push(@SortGroups,$g->{'name'}."\t".join("\t", @{$g->{'members'}})."\n"); + } +open(GROUPS, ">$groups_file"); +print GROUPS sort { lc($a) cmp lc($b) } @SortGroups; +close(GROUPS); +&automatic_backup(); +} + +# list_services([file]) +# Returns a list of services, each of which has a name and multiple +# protocols and port +sub list_services +{ +local ($sf, @rv); +#if (!-r $standard_services_file) { +# system("cp $module_root_directory/standard-services $standard_services_file"); +# } +foreach $sf ($_[0] || $services_file, $standard_services_file) { + local @frv; + open(SERVS, $sf); + while() { + s/\r|\n//g; + s/#.*$//; + s/\s+$//; + if (/^(\S+)\t+(.*)$/) { + local $serv = { 'name' => $1, + 'standard' => + ($sf eq $standard_services_file), + 'index' => scalar(@frv) }; + local @pps = split(/\s*\t+\s*/, $2); + local $i; + for($i=0; $i<@pps; $i+=2) { + if ($pps[$i] eq "other") { + push(@{$serv->{'others'}}, $pps[$i+1]); + } + else { + push(@{$serv->{'protos'}}, $pps[$i]); + push(@{$serv->{'ports'}}, $pps[$i+1]); + } + } + push(@frv, $serv); + } + } + close(SERVS); + if ($sf eq $standard_services_file) { + push(@rv, sort { lc($a->{'name'}) cmp lc($b->{'name'}) } @frv); + } + else { + push(@rv, @frv); + } + } +return @rv; +} + +# combine_services(comma-list, &services-hash) +# Returns lists of protocols and port numbers taken from a comma-separated list +# of service names +sub combine_services +{ +local (@protos, @ports); +foreach $sn (split(/,/, $_[0])) { + local $serv = $_[1]->{$sn}; + push(@protos, @{$serv->{'protos'}}); + push(@ports, @{$serv->{'ports'}}); + local ($cprotos, $cports) = &combine_services(join(",", @{$serv->{'others'}}), $_[1]); + push(@protos, @$cprotos); + push(@ports, @$cports); + } +return (\@protos, \@ports); +} + +# save_services(service, ...) +sub save_services +{ +#open(SERVS, ">$services_file"); + +local @SortGroups; +local $data; +foreach $serv (@_) { + next if ($serv->{'standard'}); + $data=$serv->{'name'}; + local $i; + for($i=0; $i<@{$serv->{'protos'}}; $i++) { + $data = $data . "\t" . $serv->{'protos'}->[$i] . "\t" . $serv->{'ports'}->[$i]; + } + for($i=0; $i<@{$serv->{'others'}}; $i++) { + if ( $serv->{'others'}->[$i] ne $serv->{'name'}) { + $data = $data . "\tother\t".$serv->{'others'}->[$i]; + } + } + $data=$data . "\n"; + push(@SortGroups,$data); + } + + +open(SERVS, ">$services_file"); +print SERVS sort { lc($a) cmp lc($b) } @SortGroups; +close(SERVS); + +} + +# list_rules([file]) +# Returns a list of rules, each of which has a source, destination, service, +# action and log-flag +sub list_rules +{ +local @rv; +open(RULES, $_[0] || $rules_file); +local $rn = 1; +while() { + s/\r|\n//g; + if (/^(#*)([^\t]+)\t+([^\t]+)\t+(\S+)\t+(\S+)\t+(\d+)(\t+(\S+))?(\t+(\S+))?$/) { + local $rule = { 'enabled' => !$1, + 'source' => $2, + 'dest' => $3, + 'service' => $4, + 'action' => $5, + 'log' => $6, + 'time' => $8 || "*", + 'desc' => &un_urlize($10 || "*"), + 'index' => scalar(@rv), + 'num' => $rn++ }; + push(@rv, $rule); + } + elsif (/^(\S+)$/) { + local $sep = { 'sep' => 1, + 'desc' => &un_urlize($1), + 'index' => scalar(@rv) }; + push(@rv, $sep); + } + } +close(RULES); +return @rv; +} + +# save_rules(rule, ...) +sub save_rules +{ +open(RULES, ">$rules_file"); +local $rule; +foreach $rule (@_) { + if ($rule->{'sep'}) { + print RULES &urlize($rule->{'desc'}),"\n"; + } + else { + print RULES ($rule->{'enabled'} ? "" : "#"), + $rule->{'source'},"\t", + $rule->{'dest'},"\t", + $rule->{'service'},"\t", + $rule->{'action'},"\t", + $rule->{'log'},"\t", + $rule->{'time'},"\t", + $rule->{'desc'} eq "*" ? "*" + : &urlize($rule->{'desc'}),"\n"; + } + } +close(RULES); +} + +# group_name(string, [direction]) +# Given a source or destination name that may be a group, makes it nice +sub group_name +{ +if ($_[0] =~ /^\@(.*)$/) { + # Host group + return "$1"; + } +elsif ($_[0] =~ /^\!\@(.*)$/) { + # Negated host group + return "".&text('not', "$1").""; + } +elsif ($_[0] =~ /^\%(.*)$/) { + # Interface + return "".&text('iface', "$1").""; + } +elsif ($_[0] =~ /^\!\%(.*)$/) { + # Negated interface + return "".&text('iface_not', "$1").""; + } +elsif ($_[0] eq '*') { + # Anywhere + return $text{'anywhere'}; + } +elsif ($_[0] eq '!*') { + # Nowhere + return $text{'nowhere'}; + } +elsif ($_[0] =~ /^\!(.*\/.*)$/) { + # Negated network address + return &text('not', "$1"); + } +elsif ($_[0] =~ /^\!([0-9\.]+)\-([0-9]+)$/) { + # Negated address range + return &text('not', "$1-$2"); + } +elsif ($_[0] =~ /^\!(.*)$/) { + # Negated hostname or IP + return &text('not', "$1"); + } +elsif ($_[0] =~ /^(.*\/.*)$/) { + # Network address + return "$_[0]"; + } +elsif ($_[0] =~ /^([0-9\.]+)\-([0-9]+)$/) { + # Address range + return "$1-$2"; + } +else { + # Hostname or IP + return "$_[0]"; + } +} + +# group_names(string) +sub group_names +{ +return join(", ", map { &group_name($_) } split(/\s+/, $_[0])); +} + +# group_names_link(dest, [from], [direction]) +sub group_names_link +{ +local $g; +local @rv; +foreach $g (split(/\s+/, $_[0])) { + if ($g =~ /^\@(.*)$/ || $g =~ /^\!\@(.*)$/) { + push(@rv, "". + &group_name($g, $_[2]).""); + } + else { + push(@rv, &group_name($g, $_[2])); + } + } +return join(", ", @rv); +} + +# group_input(name, [value], [blankoption], [multiple]) +sub group_input +{ +local @groups = &list_groups(); +return undef if (!@groups); +local $rv = $_[3] ? "\n"; +if ($_[2]) { + $rv .= sprintf "