Initial check-in of open-sourced firewall module
authorJamie Cameron <jcameron@webmin.com>
Sat, 26 Mar 2011 00:11:27 +0000 (17:11 -0700)
committerJamie Cameron <jcameron@webmin.com>
Sat, 26 Mar 2011 00:11:27 +0000 (17:11 -0700)
115 files changed:
itsecur-firewall/CbButton.class [new file with mode: 0644]
itsecur-firewall/CbButton.java [new file with mode: 0644]
itsecur-firewall/CbButtonCallback.class [new file with mode: 0644]
itsecur-firewall/CbButtonGroup.class [new file with mode: 0644]
itsecur-firewall/LineInputStream.class [new file with mode: 0644]
itsecur-firewall/LineInputStream.java [new file with mode: 0755]
itsecur-firewall/LogViewer.class [new file with mode: 0644]
itsecur-firewall/LogViewer.java [new file with mode: 0644]
itsecur-firewall/Makefile [new file with mode: 0644]
itsecur-firewall/Util.class [new file with mode: 0644]
itsecur-firewall/Util.java [new file with mode: 0644]
itsecur-firewall/acl_security.pl [new file with mode: 0644]
itsecur-firewall/apply.cgi [new file with mode: 0755]
itsecur-firewall/apply.pl [new file with mode: 0755]
itsecur-firewall/authdownload.cgi [new file with mode: 0755]
itsecur-firewall/authtail.cgi [new file with mode: 0755]
itsecur-firewall/backup.cgi [new file with mode: 0755]
itsecur-firewall/backup.pl [new file with mode: 0755]
itsecur-firewall/bootup.cgi [new file with mode: 0755]
itsecur-firewall/config-*-linux [new file with mode: 0644]
itsecur-firewall/config-freebsd [new file with mode: 0644]
itsecur-firewall/config.info [new file with mode: 0644]
itsecur-firewall/debug_file [new file with mode: 0644]
itsecur-firewall/defaultacl [new file with mode: 0644]
itsecur-firewall/down.cgi [new file with mode: 0755]
itsecur-firewall/download.cgi [new file with mode: 0755]
itsecur-firewall/edit_group.cgi [new file with mode: 0755]
itsecur-firewall/edit_rule.cgi [new file with mode: 0755]
itsecur-firewall/edit_sep.cgi [new file with mode: 0755]
itsecur-firewall/edit_service.cgi [new file with mode: 0755]
itsecur-firewall/edit_time.cgi [new file with mode: 0755]
itsecur-firewall/edit_user.cgi [new file with mode: 0755]
itsecur-firewall/enable_rules.cgi [new file with mode: 0755]
itsecur-firewall/images/.xvpics/backup.gif [new file with mode: 0644]
itsecur-firewall/images/.xvpics/icon.gif [new file with mode: 0644]
itsecur-firewall/images/.xvpics/restore.gif [new file with mode: 0644]
itsecur-firewall/images/.xvpics/syn.gif [new file with mode: 0644]
itsecur-firewall/images/authlogs.gif [new file with mode: 0644]
itsecur-firewall/images/backup.gif [new file with mode: 0644]
itsecur-firewall/images/bandwidth.gif [new file with mode: 0755]
itsecur-firewall/images/down.gif [new file with mode: 0644]
itsecur-firewall/images/gap.gif [new file with mode: 0644]
itsecur-firewall/images/groups.gif [new file with mode: 0644]
itsecur-firewall/images/icon.gif [new file with mode: 0644]
itsecur-firewall/images/import.gif [new file with mode: 0644]
itsecur-firewall/images/lleft.gif [new file with mode: 0644]
itsecur-firewall/images/logs.gif [new file with mode: 0644]
itsecur-firewall/images/nat.gif [new file with mode: 0644]
itsecur-firewall/images/nat2.gif [new file with mode: 0644]
itsecur-firewall/images/pat.gif [new file with mode: 0644]
itsecur-firewall/images/remote.gif [new file with mode: 0644]
itsecur-firewall/images/report.gif [new file with mode: 0644]
itsecur-firewall/images/restore.gif [new file with mode: 0644]
itsecur-firewall/images/rright.gif [new file with mode: 0644]
itsecur-firewall/images/rules.gif [new file with mode: 0644]
itsecur-firewall/images/services.gif [new file with mode: 0644]
itsecur-firewall/images/smallicon.gif [new file with mode: 0644]
itsecur-firewall/images/spoof.gif [new file with mode: 0644]
itsecur-firewall/images/syn.gif [new file with mode: 0644]
itsecur-firewall/images/times.gif [new file with mode: 0644]
itsecur-firewall/images/top_r1_c1.jpg [new file with mode: 0644]
itsecur-firewall/images/up.gif [new file with mode: 0644]
itsecur-firewall/images/users.gif [new file with mode: 0644]
itsecur-firewall/import_groups.cgi [new file with mode: 0755]
itsecur-firewall/import_rules.cgi [new file with mode: 0755]
itsecur-firewall/import_servs.cgi [new file with mode: 0755]
itsecur-firewall/import_times.cgi [new file with mode: 0755]
itsecur-firewall/index.cgi [new file with mode: 0755]
itsecur-firewall/ipf-lib.pl [new file with mode: 0644]
itsecur-firewall/iptables-lib.pl [new file with mode: 0644]
itsecur-firewall/itsecur-lib.pl [new file with mode: 0644]
itsecur-firewall/lang/en [new file with mode: 0644]
itsecur-firewall/list_authlogs.cgi [new file with mode: 0755]
itsecur-firewall/list_backup.cgi [new file with mode: 0755]
itsecur-firewall/list_bandwidth.cgi [new file with mode: 0755]
itsecur-firewall/list_groups.cgi [new file with mode: 0755]
itsecur-firewall/list_import.cgi [new file with mode: 0755]
itsecur-firewall/list_logs.cgi [new file with mode: 0755]
itsecur-firewall/list_nat.cgi [new file with mode: 0755]
itsecur-firewall/list_nat2.cgi [new file with mode: 0755]
itsecur-firewall/list_pat.cgi [new file with mode: 0755]
itsecur-firewall/list_remote.cgi [new file with mode: 0755]
itsecur-firewall/list_report.cgi [new file with mode: 0755]
itsecur-firewall/list_restore.cgi [new file with mode: 0755]
itsecur-firewall/list_rules.cgi [new file with mode: 0755]
itsecur-firewall/list_services.cgi [new file with mode: 0755]
itsecur-firewall/list_spoof.cgi [new file with mode: 0755]
itsecur-firewall/list_syn.cgi [new file with mode: 0755]
itsecur-firewall/list_times.cgi [new file with mode: 0755]
itsecur-firewall/list_users.cgi [new file with mode: 0755]
itsecur-firewall/list_welf.cgi [new file with mode: 0755]
itsecur-firewall/log_parser.pl [new file with mode: 0644]
itsecur-firewall/migrate-groups.pl [new file with mode: 0644]
itsecur-firewall/module.info [new file with mode: 0644]
itsecur-firewall/restore.cgi [new file with mode: 0755]
itsecur-firewall/save_bandwidth.cgi [new file with mode: 0755]
itsecur-firewall/save_group.cgi [new file with mode: 0755]
itsecur-firewall/save_nat.cgi [new file with mode: 0755]
itsecur-firewall/save_nat2.cgi [new file with mode: 0755]
itsecur-firewall/save_pat.cgi [new file with mode: 0755]
itsecur-firewall/save_remote.cgi [new file with mode: 0755]
itsecur-firewall/save_rule.cgi [new file with mode: 0755]
itsecur-firewall/save_search.cgi [new file with mode: 0755]
itsecur-firewall/save_sep.cgi [new file with mode: 0755]
itsecur-firewall/save_service.cgi [new file with mode: 0755]
itsecur-firewall/save_spoof.cgi [new file with mode: 0755]
itsecur-firewall/save_syn.cgi [new file with mode: 0755]
itsecur-firewall/save_time.cgi [new file with mode: 0755]
itsecur-firewall/save_user.cgi [new file with mode: 0755]
itsecur-firewall/standard-services [new file with mode: 0644]
itsecur-firewall/status_monitor.pl [new file with mode: 0644]
itsecur-firewall/stop.pl [new file with mode: 0755]
itsecur-firewall/tail.cgi [new file with mode: 0755]
itsecur-firewall/up.cgi [new file with mode: 0755]
itsecur-firewall/welf.cgi [new file with mode: 0755]

diff --git a/itsecur-firewall/CbButton.class b/itsecur-firewall/CbButton.class
new file mode 100644 (file)
index 0000000..5f15e39
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 (file)
index 0000000..9770751
--- /dev/null
@@ -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<buttons.size(); i++) {
+               CbButton but = (CbButton)buttons.elementAt(i);
+               but.selected = (b == but);
+               but.repaint();
+               }
+       }
+}
+
diff --git a/itsecur-firewall/CbButtonCallback.class b/itsecur-firewall/CbButtonCallback.class
new file mode 100644 (file)
index 0000000..86b7935
Binary files /dev/null and b/itsecur-firewall/CbButtonCallback.class differ
diff --git a/itsecur-firewall/CbButtonGroup.class b/itsecur-firewall/CbButtonGroup.class
new file mode 100644 (file)
index 0000000..5f1aff2
Binary files /dev/null and b/itsecur-firewall/CbButtonGroup.class differ
diff --git a/itsecur-firewall/LineInputStream.class b/itsecur-firewall/LineInputStream.class
new file mode 100644 (file)
index 0000000..108beef
Binary files /dev/null and b/itsecur-firewall/LineInputStream.class differ
diff --git a/itsecur-firewall/LineInputStream.java b/itsecur-firewall/LineInputStream.java
new file mode 100755 (executable)
index 0000000..5b93116
--- /dev/null
@@ -0,0 +1,81 @@
+// LineInputStream
+// A stream with some useful stdio-like methods. Can be used either for
+// inheriting those methods into your own input stream, or for adding them
+// to some input stream.
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+
+public class LineInputStream 
+{
+       InputStream in;
+
+       LineInputStream(InputStream i)
+               { in = i; }
+       LineInputStream()
+               { }
+
+       public int read() throws IOException
+               { return in.read(); }
+       public int read(byte b[]) throws IOException
+               { return in.read(b); }
+       public int read(byte b[], int o, int l) throws IOException
+               { return in.read(b, o, l); }
+       public long skip(long n) throws IOException
+               { return in.skip(n); }
+       public int available() throws IOException
+               { return in.available(); }
+       public void close() throws IOException
+               { in.close(); }
+       public synchronized void mark(int readlimit)
+               { in.mark(readlimit); }
+       public synchronized void reset() throws IOException
+               { in.reset(); }
+       public boolean markSupported()
+               { return in.markSupported(); }
+
+       // gets
+       // Read a line and return it (minus the \n)
+       String gets() throws IOException, EOFException
+       {
+       StringBuffer buf = new StringBuffer();
+       int b;
+       while((b = read()) != '\n') {
+               if (b == -1) throw new EOFException();
+               buf.append((char)b);
+               }
+       if (buf.length() != 0 && buf.charAt(buf.length()-1) == '\r')
+               buf.setLength(buf.length()-1);  // lose \r
+       return buf.toString();
+       }
+
+       // getw
+       // Read a single word, surrounded by whitespace
+       String getw() throws IOException, EOFException
+       {
+       StringBuffer buf = new StringBuffer();
+       // skip spaces
+       int b;
+       do {
+               if ((b = read()) == -1) throw new EOFException();
+               } while(Character.isSpace((char)b));
+       // add characters
+       do {
+               buf.append((char)b);
+               if ((b = read()) == -1) throw new EOFException();
+               } while(!Character.isSpace((char)b));
+       return buf.toString();
+       }
+
+       // readdata
+       // Fill the given array completely, even if read() only reads
+       // some max number of bytes at a time.
+       public int readdata(byte b[]) throws IOException, EOFException
+       {
+       int p = 0;
+       while(p < b.length)
+               p += read(b, p, b.length-p);
+       return b.length;
+       }
+}
+
diff --git a/itsecur-firewall/LogViewer.class b/itsecur-firewall/LogViewer.class
new file mode 100644 (file)
index 0000000..437b212
Binary files /dev/null and b/itsecur-firewall/LogViewer.class differ
diff --git a/itsecur-firewall/LogViewer.java b/itsecur-firewall/LogViewer.java
new file mode 100644 (file)
index 0000000..51cd732
--- /dev/null
@@ -0,0 +1,133 @@
+import java.awt.*;
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import java.applet.*;
+
+public class LogViewer extends Applet implements Runnable,CbButtonCallback
+{
+       TextArea log;
+       StringBuffer logbuffer = new StringBuffer();
+       LineInputStream is;
+       Thread th;
+       CbButton pause, button;
+       boolean paused = false;
+
+       public void init()
+       {
+       // Create the UI
+       setLayout(new BorderLayout());
+       add("Center", log = new TextArea());
+       log.setEditable(false);
+       Util.setFont(new Font("TimesRoman", Font.PLAIN, 12));
+       Panel bot = new Panel();
+       bot.setBackground(Color.white);
+       bot.setForeground(Color.white);
+       bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
+       if (getParameter("pause") != null) {
+               // Add button to pause display
+               bot.add(pause = new CbButton("  Pause  ", this));
+               }
+       if (getParameter("buttonname") != null) {
+               // Add button for some other purpose
+               bot.add(button = new CbButton(getParameter("buttonname"),this));
+               }
+       add("South", bot);
+       }
+
+       public void start()
+       {
+       // Start download thread
+       log.setText("");
+       th = new Thread(this);
+       th.start();
+       }
+
+       public void stop()
+       {
+       // Stop download
+       try {
+               String killurl = getParameter("killurl");
+               if (killurl != null) {
+                       // Call this CGI at stop time
+                       try {
+                               URL u = new URL(getDocumentBase(), killurl);
+                               URLConnection uc = u.openConnection();
+                               String session = getParameter("session");
+                               if (session != null)
+                                   uc.setRequestProperty("Cookie", session);
+                               uc.getInputStream().close();
+                               }
+                       catch(Exception e2) { }
+                       }
+               if (is != null) is.close();
+               if (th != null) th.stop();
+               }
+       catch(Exception e) {
+               // ignore it
+               e.printStackTrace();
+               }
+       }
+
+       public void run()
+       {
+       try {
+               URL u = new URL(getDocumentBase(), getParameter("url"));
+               URLConnection uc = u.openConnection();
+               String session = getParameter("session");
+               if (session != null)
+                       uc.setRequestProperty("Cookie", session);
+               is = new LineInputStream(uc.getInputStream());
+               while(true) {
+                       String l = is.gets();
+                       append(l);
+                       }
+               }
+       catch(EOFException e) {
+               // end of file ..
+               }
+       catch(IOException e) {
+               // shouldn't happen!
+               e.printStackTrace();
+               append("IO error : "+e.getMessage());
+               }
+       }
+
+       int len = 0, oldlen = 0;
+
+       void append(String str) {
+               if (!paused) {
+                       log.append((len == 0 ? "" : "\n")+str);
+                       }
+               logbuffer.append((len == 0 ? "" : "\n")+str);
+               oldlen = len;
+               len += str.length()+1;
+               if (!paused) {
+                       log.select(oldlen, oldlen);
+                       }
+       }
+
+       public void click(CbButton b) {
+               if (b == pause) {
+                       if (paused) {
+                               // Resume display, and append missing text
+                               pause.setText("  Pause  ");
+                               log.setText(logbuffer.toString());
+                               log.select(oldlen, oldlen);
+                       } else {
+                               // Stop display
+                               pause.setText("Resume");
+                       }
+                       paused = !paused;
+               } else if (b == button) {
+                       // Open some page
+                       try {
+                               URL u = new URL(getDocumentBase(),
+                                               getParameter("buttonlink"));
+                               getAppletContext().showDocument(u);
+                               }
+                       catch(Exception e) { }
+               }
+       }
+}
+
diff --git a/itsecur-firewall/Makefile b/itsecur-firewall/Makefile
new file mode 100644 (file)
index 0000000..50006ac
--- /dev/null
@@ -0,0 +1,2 @@
+LogViewer.class:       LogViewer.java
+                       javac -target 1.2 -source 1.2 -classpath . *.java
diff --git a/itsecur-firewall/Util.class b/itsecur-firewall/Util.class
new file mode 100644 (file)
index 0000000..cb1d4cf
Binary files /dev/null and b/itsecur-firewall/Util.class differ
diff --git a/itsecur-firewall/Util.java b/itsecur-firewall/Util.java
new file mode 100644 (file)
index 0000000..95d9a7d
--- /dev/null
@@ -0,0 +1,148 @@
+import java.awt.*;
+import java.awt.image.*;
+
+class Util
+{
+       static Frame fr;
+       static Graphics g;
+       static Font f;
+       static FontMetrics fnm;
+       static Toolkit tk;
+
+       static Color light_edge = Color.white;
+       static Color dark_edge = Color.black;
+       static Color body = Color.lightGray;
+       static Color body_hi = new Color(210, 210, 210);
+       static Color light_edge_hi = Color.white;
+       static Color dark_edge_hi = Color.darkGray;
+       static Color dark_bg = new Color(150, 150, 150);
+       static Color text = Color.black;
+       static Color light_bg = Color.white;
+
+       static
+       {
+       fr = new Frame();
+       fr.addNotify();
+       g = fr.getGraphics();
+       setFont(new Font("TimesRoman", Font.PLAIN, 8));
+       tk = Toolkit.getDefaultToolkit();
+       }
+
+       static boolean waitForImage(Image i)
+       {
+       MediaTracker mt = new MediaTracker(fr);
+       mt.addImage(i, 0);
+       try { mt.waitForAll(); } catch(Exception e) { return false; }
+       return !mt.isErrorAny();
+       }
+
+       static boolean waitForImage(Image i, int w, int h)
+       {
+       MediaTracker mt = new MediaTracker(fr);
+       mt.addImage(i, w, h, 0);
+       try { mt.waitForAll(); } catch(Exception e) { return false; }
+       return !mt.isErrorAny();
+       }
+
+       static int getWidth(Image i)
+       {
+       waitForImage(i);
+       return i.getWidth(fr);
+       }
+
+       static int getHeight(Image i)
+       {
+       waitForImage(i);
+       return i.getHeight(fr);
+       }
+
+       static Image createImage(int w, int h)
+       {
+       return fr.createImage(w, h);
+       }
+
+       static Image createImage(ImageProducer p)
+       {
+       return fr.createImage(p);
+       }
+
+       static Object createObject(String name)
+       {
+       try {
+               Class c = Class.forName(name);
+               return c.newInstance();
+               }
+       catch(Exception e) {
+               System.err.println("Failed to create object "+name+" : "+
+                                  e.getClass().getName());
+               System.exit(1);
+               }
+       return null;
+       }
+
+       /**Create a new instance of some object
+        */
+       static Object createObject(Object o)
+       {
+       try { return o.getClass().newInstance(); }
+       catch(Exception e) {
+               System.err.println("Failed to reproduce object "+o+" : "+
+                                e.getClass().getName());
+               System.exit(1);
+               }
+       return null;
+       }
+
+
+       static void dottedRect(Graphics g, int x1, int y1,
+                              int x2, int y2, int s)
+       {
+       int i, s2 = s*2, t;
+       if (x2 < x1) { t = x1; x1 = x2; x2 = t; }
+       if (y2 < y1) { t = y1; y1 = y2; y2 = t; }
+       for(i=x1; i<=x2; i+=s2)
+               g.drawLine(i, y1, i+s > 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<c.countComponents(); i++) {
+               Component cc = c.getComponent(i);
+               if (cc instanceof Container)
+                       recursiveLayout((Container)cc);
+               }
+       }
+
+       static void recursiveBackground(Component c, Color b)
+       {
+       if (c instanceof TextField || c instanceof Choice ||
+           c instanceof TextArea)
+               return;         // leave these alone
+       c.setBackground(b);
+       if (c instanceof Container) {
+               Container cn = (Container)c;
+               for(int i=0; i<cn.countComponents(); i++)
+                       recursiveBackground(cn.getComponent(i), b);
+               }
+       }
+
+       static void recursiveBody(Component c)
+       {
+       recursiveBackground(c, Util.body);
+       }
+
+       static void setFont(Font nf)
+       {
+       f = nf;
+       g.setFont(f);
+       fnm = g.getFontMetrics();
+       }
+}
+
diff --git a/itsecur-firewall/acl_security.pl b/itsecur-firewall/acl_security.pl
new file mode 100644 (file)
index 0000000..fe3292c
--- /dev/null
@@ -0,0 +1,58 @@
+
+if (!defined($services_file)) {
+       do 'itsecur-lib.pl';
+       }
+
+# acl_security_form(&options)
+# Output HTML for editing security options for the acl module
+sub acl_security_form
+{
+# Work out which features can be edited and which can be read
+local (@edit, @read);
+if (defined($_[0]->{'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 "<tr> <td valign=top><b>",$text{'acl_'.$w->[1]},
+             "</b></td> <td>\n";
+       printf "<input type=radio name=$w->[2] value=1 %s> %s\n",
+               $can{"*"} ? "checked" : "", $text{'acl_all'};
+       printf "<input type=radio name=$w->[2] value=0 %s> %s<br>\n",
+               $can{"*"} ? "" : "checked", $text{'acl_sel'};
+       printf "<select name=$w->[1] multiple size=%d>\n",
+               scalar(@opts);
+       foreach $o (@opts, 'apply', 'bootup') {
+               printf "<option value=%s %s>%s\n",
+                       $o, $can{$o} ? "selected" : "",
+                       $text{"acl_".$o} || $text{$o."_title"};
+               }
+       print "</select></td> </tr>\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 (executable)
index 0000000..b0dfc98
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<p>$text{'apply_doing'}<br>\n";
+&enable_routing();
+$err = &apply_rules();
+if ($err) {
+       print &text('apply_failed', $err),"<p>\n";
+       }
+else {
+       print "$text{'apply_done'}<p>\n";
+       }
+
+print "<hr>\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 (executable)
index 0000000..d0ec49a
--- /dev/null
@@ -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 (executable)
index 0000000..07e3586
--- /dev/null
@@ -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(<LOG>) {
+       print $_ if (!&is_log_line($_));
+       }
+close(LOG);
+
diff --git a/itsecur-firewall/authtail.cgi b/itsecur-firewall/authtail.cgi
new file mode 100755 (executable)
index 0000000..c60fa9e
--- /dev/null
@@ -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(<LOG>) {
+       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 = <LOG>;
+       print $line if ($line && !&is_log_line($line));
+       }
+
diff --git a/itsecur-firewall/backup.cgi b/itsecur-firewall/backup.cgi
new file mode 100755 (executable)
index 0000000..e7d732d
--- /dev/null
@@ -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 "<hr>\n";
+
+       print "<p>",&text('backup_donesched'),"<p>\n";
+
+       print "<hr>\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(<FILE>) {
+               print;
+               }
+       close(FILE);
+       unlink($file);
+       &remote_webmin_log("backup");
+       }
+else {
+       # Tell the user
+       &header($text{'backup_title'}, "",
+               undef, undef, undef, undef, &apply_button());
+       print "<hr>\n";
+
+       print "<p>$done<p>\n";
+
+       print "<hr>\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 (executable)
index 0000000..7902426
--- /dev/null
@@ -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 (executable)
index 0000000..2540efc
--- /dev/null
@@ -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 (file)
index 0000000..7697ce1
--- /dev/null
@@ -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 (file)
index 0000000..1637440
--- /dev/null
@@ -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 (file)
index 0000000..bf3aac6
--- /dev/null
@@ -0,0 +1,15 @@
+line1=Configurable options,11
+fw_any=Include firewall in <b>Any</b> 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 (file)
index 0000000..d2ba40b
--- /dev/null
@@ -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 (file)
index 0000000..baf1e48
--- /dev/null
@@ -0,0 +1,2 @@
+features=*
+rfeatures=*
diff --git a/itsecur-firewall/down.cgi b/itsecur-firewall/down.cgi
new file mode 100755 (executable)
index 0000000..a8fd47f
--- /dev/null
@@ -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 (executable)
index 0000000..fda4b20
--- /dev/null
@@ -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(<LOG>) {
+       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 (executable)
index 0000000..f253675
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<form action=save_group.cgi>\n";
+print "<input type=hidden name=new value='$in{'new'}'>\n";
+print "<input type=hidden name=idx value='$in{'idx'}'>\n";
+print "<input type=hidden name=from value='$in{'from'}'>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'group_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+print "<tr> <td><b>$text{'group_name'}</b></td>\n";
+printf "<td><input name=name size=20 value='%s'></td> </tr>\n",
+       $group->{'name'};
+
+print "<tr> <td valign=top><b>$text{'group_members'}</b></td>\n";
+print "<td><table>\n";
+$i = 0;
+foreach $m (( grep { !/\!?\@/ } @{$group->{'members'}} ),
+           $blank, $blank, $blank, $blank, $blank, $blank) {
+       $neg = ($m =~ s/^\!//);
+       print "<input name=member_$i size=40 value='$m'>\n";
+       print "<input type=checkbox name=neg_$i value=! ",
+             $neg ? "checked" : "","> $text{'group_neg'}<br>\n";
+       $i++;
+       }
+print "</table>\n";
+print "<input type=checkbox name=resolv value=1> $text{'group_resolv'}\n";
+print "</td> </tr>\n";
+
+# Show member groups
+print "<tr> <td valign=top><b>$text{'group_members2'}</b></td>\n";
+print "<td><table>\n";
+$i = 0;
+foreach $m (( grep { /\!?\@/ } @{$group->{'members'}} ),
+           $blank, $blank, $blank, $blank, $blank, $blank) {
+       $neg = ($m =~ s/^\!//);
+       $m =~ s/^\@//;
+       print "<tr> <td>\n";
+       print &group_input("group_$i", $m, 1);
+       print "</td> </tr>\n";
+       $i++;
+       }
+print "</table></td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+if ($in{'new'}) {
+       print "<input type=submit value='$text{'create'}'>\n";
+       }
+else {
+       print "<input type=submit value='$text{'save'}'>\n";
+       print "<input type=submit name=delete value='$text{'delete'}'>\n";
+       }
+print "</form>\n";
+&can_edit_disable("groups");
+
+print "<hr>\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 (executable)
index 0000000..d541d84
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<form action=save_rule.cgi>\n";
+print "<input type=hidden name=new value='$in{'new'}'>\n";
+print "<input type=hidden name=idx value='$in{'idx'}'>\n";
+print "<input type=hidden name=insert value='$in{'insert'}'>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'rule_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+# Show comment
+print "<tr> <td valign=top><b>$text{'rule_desc'}</b></td> <td colspan=2>\n";
+printf "<input name=desc size=60 value='%s'></td> </tr>\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 "<tr> <td valign=top><b>",$text{'rule_'.$s},
+             "</b></td> <td colspan=2>\n";
+       print "<table>\n";
+       print "<tr><td colspan=2>";
+       printf "<input type=radio name=${s}_mode value=0 %s> %s\n",
+               $sm == 0 ? "checked" : "",
+               $text{'rule_anywhere'};
+       print "</td></tr>\n";
+
+       # Specific host option
+       print "<tr><td valign=top>";
+       printf "<input type=radio name=${s}_mode value=1 %s> %s\n",
+               $sm == 1 ? "checked" : "", $text{'rule_host'};
+       print "</td><td>";
+       printf "<input name=${s}_host size=30 value='%s'>\n",
+               $sm == 1 ? $rule->{$s} : "";
+       print "$text{'rule_named'}\n";
+       print "<input name=${s}_name size=15><br>\n";
+       print "<input type=checkbox name=${s}_resolv value=1> ",
+             "$text{'rule_resolv'}\n";
+       print "</td></tr>\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 "<tr><td valign=top>";
+               printf "<input type=radio name=${s}_mode value=2 %s> %s\n",
+                       $sm == 2 ? "checked" : "", $text{'rule_group'};
+               print "</td><td>";
+               print $gi;
+               print "</td></tr>\n";
+               }
+
+       # Interface option
+       $ii = &iface_input("${s}_iface",
+                          $rule->{$s} =~ /^\%(.*)$/ ? $1 : undef);
+       if ($ii || $sm == 3) {
+               print "<tr><td>";
+               printf "<input type=radio name=${s}_mode value=3 %s> %s\n",
+                       $sm == 3 ? "checked" : "", $text{'rule_iface'};
+               print "</td><td>";
+               print $ii;
+               print "</td></tr>\n";
+               }
+
+       print "</table>\n";
+       print "</td> <td valign=top>\n";
+       #printf "<input type=checkbox name=${s}_not value=1 %s> %s\n",
+       #       $not ? "checked" : "", $text{'rule_not'};
+       print "</td> </tr>\n";
+       }
+
+# Show service
+$not = ($rule->{'service'} =~ s/^!//g);
+print "<tr> <td valign=top><b>$text{'rule_service'}</b></td> <td>\n";
+printf "<input type=radio name=service_mode value=0 %s> %s\n",
+       $rule->{'service'} eq '*' ? "checked" : "", $text{'rule_anyserv'};
+printf "<input type=radio name=service_mode value=1 %s> %s<br>\n",
+       $rule->{'service'} eq '*' ? "" : "checked", $text{'rule_oneserv'};
+print &service_input("service",
+                    $rule->{'service'} eq '*' ? undef : $rule->{'service'},
+                    0, 1);
+print "</td> <td valign=top>\n";
+#printf "<input type=checkbox name=snot value=1 %s> %s\n",
+#      $not ? "checked" : "", $text{'rule_not'};
+print "</td> </tr>\n";
+
+# Show action upon match
+print "<tr> <td valign=top><b>$text{'rule_action'}</b></td> <td>\n";
+print &action_input("action", $rule->{'action'});
+print "</td> <td>\n";
+printf "<input type=checkbox name=log value=1 %s> %s\n",
+       $rule->{'log'} ? 'checked' : '', $text{'rule_log'};
+print "</td> </tr>\n";
+
+# Show time that this rule applies
+$inp = &time_input("time", $rule->{'time'} eq "*" ? undef : $rule->{'time'});
+if ($inp) {
+       print "<tr> <td valign=top><b>$text{'rule_time'}</b></td> <td>";
+       printf "<input type=radio name=time_def value=1 %s> %s\n",
+               $rule->{'time'} eq "*" ? "checked" : "", $text{'rule_anytime'};
+       printf "<input type=radio name=time_def value=0 %s> %s\n",
+               $rule->{'time'} eq "*" ? "" : "checked", $text{'rule_seltime'};
+       print $inp;
+       print "</td> </tr>\n";
+       }
+else {
+       print "<input type=hidden name=time_def value=1>\n";
+       }
+
+# Show enabled flag
+print "<tr> <td valign=top><b>$text{'rule_enabled'}</b></td> <td>\n";
+printf "<input type=radio name=enabled value=1 %s> %s\n",
+       $rule->{'enabled'} ? "checked" : "", $text{'yes'};
+printf "<input type=radio name=enabled value=0 %s> %s\n",
+       $rule->{'enabled'} ? "" : "checked", $text{'no'};
+print "</td> </tr>\n";
+
+# Show input for position of rule
+print "<tr> <td><b>$text{'rule_atpos'}</b></td> <td>\n";
+print "<select name=pos>\n";
+foreach $br (@rules) {
+       next if ($br eq $rule);
+       if ($br->{'sep'}) {
+               printf "<option value=%s %s>%s\n",
+                       $br->{'index'},
+                       !$in{'new'} &&
+                       $rule->{'index'} == $br->{'index'}-1 ? "selected" : "",
+                       &text('rule_spos', $br->{'desc'});
+               }
+       else {
+               printf "<option value=%s %s>%s\n",
+                       $br->{'index'},
+                       !$in{'new'} &&
+                       $rule->{'index'} == $br->{'index'}-1 ? "selected" : "",
+                       &text('rule_pos', $br->{'num'},
+                             &group_name($br->{'source'}),
+                             &group_name($br->{'dest'}));
+               }
+       }
+printf "<option value=%s %s>%s\n",
+       -1, $in{'new'} || $rule eq $rules[$#rules] ? "selected" : "",
+       $text{'rule_end'};
+print "</select></td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+if ($in{'new'}) {
+       print "<input type=submit value='$text{'create'}'>\n";
+       }
+else {
+       print "<input type=submit value='$text{'save'}'>\n";
+       print "<input type=submit name=delete value='$text{'delete'}'>\n";
+       }
+print "</form>\n";
+&can_edit_disable("rules");
+
+print "<hr>\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 (executable)
index 0000000..484b458
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<form action=save_sep.cgi>\n";
+print "<input type=hidden name=new value='$in{'new'}'>\n";
+print "<input type=hidden name=idx value='$in{'idx'}'>\n";
+print "<input type=hidden name=insert value='$in{'insert'}'>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'sep_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+# Show separator title
+print "<tr> <td valign=top><b>$text{'sep_desc'}</b></td> <td>\n";
+printf "<input name=desc size=60 value='%s'></td> </tr>\n",
+       $rule->{'desc'} eq "*" ? "" : $rule->{'desc'};
+
+# Show input for position of rule
+print "<tr> <td><b>$text{'rule_atpos'}</b></td> <td>\n";
+print "<select name=pos>\n";
+foreach $br (@rules) {
+       next if ($br eq $rule);
+       if ($br->{'sep'}) {
+               printf "<option value=%s %s>%s\n",
+                       $br->{'index'},
+                       !$in{'new'} &&
+                       $rule->{'index'} == $br->{'index'}-1 ? "selected" : "",
+                       &text('rule_spos', $br->{'desc'});
+               }
+       else {
+               printf "<option value=%s %s>%s\n",
+                       $br->{'index'},
+                       !$in{'new'} &&
+                       $rule->{'index'} == $br->{'index'}-1 ? "selected" : "",
+                       &text('rule_pos', $br->{'num'},
+                             &group_name($br->{'source'}),
+                             &group_name($br->{'dest'}));
+               }
+       }
+printf "<option value=%s %s>%s\n",
+       -1, $in{'new'} || $rule eq $rules[$#rules] ? "selected" : "",
+       $text{'rule_end'};
+print "</select></td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+if ($in{'new'}) {
+       print "<input type=submit value='$text{'create'}'>\n";
+       }
+else {
+       print "<input type=submit value='$text{'save'}'>\n";
+       print "<input type=submit name=delete value='$text{'delete'}'>\n";
+       }
+print "</form>\n";
+&can_edit_disable("rules");
+
+print "<hr>\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 (executable)
index 0000000..aae6e24
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<form action=save_service.cgi>\n";
+print "<input type=hidden name=new value='$in{'new'}'>\n";
+print "<input type=hidden name=idx value='$in{'idx'}'>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'service_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+# Show service name input
+print "<tr> <td><b>$text{'service_name'}</b></td>\n";
+printf "<td><input name=name size=20 value='%s'></td> </tr>\n",
+       $service->{'name'};
+
+# Show protocols and ports
+print "<tr> <td valign=top><b>$text{'service_ports'}</b></td>\n";
+print "<td><table border>\n";
+print "<tr $tb> <td><b>$text{'service_proto'}</b></td> ",
+      "<td><b>$text{'service_port'}</b></td> </tr>\n";
+for($i=0; $i<@{$service->{'protos'}}+6; $i++) {
+       print "<tr>\n";
+       print "<td>",&protocol_input(
+               "proto_$i", $service->{'protos'}->[$i]),"</td>\n";
+       printf "<td><input name=port_%d size=20 value='%s'></td>\n",
+               $i, $service->{'ports'}->[$i];
+       print "</tr>\n";
+       }
+print "</table></td> </tr>\n";
+
+# Show member services
+print "<tr> <td valign=top><b>$text{'service_members'}</b></td>\n";
+print "<td>",&service_input("others",
+               join(",", @{$service->{'others'}}), 0, 1),"</td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+if ($in{'new'}) {
+       print "<input type=submit value='$text{'create'}'>\n";
+       }
+else {
+       print "<input type=submit value='$text{'save'}'>\n";
+       print "<input type=submit name=delete value='$text{'delete'}'>\n";
+       }
+print "</form>\n";
+&can_edit_disable("services");
+
+print "<hr>\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 (executable)
index 0000000..44824d2
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<form action=save_time.cgi>\n";
+print "<input type=hidden name=new value='$in{'new'}'>\n";
+print "<input type=hidden name=idx value='$in{'idx'}'>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'time_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+# Show range name
+print "<tr> <td><b>$text{'time_name'}</b></td>\n";
+printf "<td><input name=name size=20 value='%s'></td> </tr>\n",
+       $time->{'name'};
+
+# Show hour range
+print "<tr> <td><b>$text{'time_hours'}</b></td> <td>\n";
+printf "<input type=radio name=hours_def value=1 %s> %s\n",
+       $time->{'hours'} eq "*" ? "checked" : "", $text{'time_allday'};
+printf "<input type=radio name=hours_def value=0 %s>\n",
+       $time->{'hours'} eq "*" ? "" : "checked";
+($from, $to) = $time->{'hours'} eq "*" ? ( ) : split(/\-/, $time->{'hours'});
+printf "%s <input name=from size=6 value='%s'>\n",
+       $text{'time_from'}, $from;
+printf "%s <input name=to size=6 value='%s'></td> </tr>\n",
+       $text{'time_to'}, $to;
+
+# Show days of week
+print "<tr> <td valign=top><b>$text{'time_days'}</b></td> <td>\n";
+printf "<input type=radio name=days_def value=1 %s> %s\n",
+       $time->{'days'} eq "*" ? "checked" : "", $text{'time_allweek'};
+printf "<input type=radio name=days_def value=0 %s> %s<br>\n",
+       $time->{'days'} eq "*" ? "" : "checked", $text{'time_sel'};
+%days = map { $_, 1 } split(/,/, $time->{'days'});
+print "<select name=days size=7 multiple>\n";
+for($i=0; $i<7; $i++) {
+       printf "<option value=%s %s>%s\n",
+               $i, $days{$i} ? "selected" : "", $text{'day_'.$i};
+       }
+print "</select></td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+if ($in{'new'}) {
+       print "<input type=submit value='$text{'create'}'>\n";
+       }
+else {
+       print "<input type=submit value='$text{'save'}'>\n";
+       print "<input type=submit name=delete value='$text{'delete'}'>\n";
+       }
+print "</form>\n";
+&can_edit_disable("times");
+
+print "<hr>\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 (executable)
index 0000000..51f04bb
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<form action=save_user.cgi>\n";
+print "<input type=hidden name=new value='$in{'new'}'>\n";
+print "<input type=hidden name=old value='$in{'name'}'>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'user_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+# Show username
+print "<tr> <td nowrap><b>$text{'user_name'}</b></td> <td>\n";
+printf "<input name=name size=20 value='%s'></td> </tr>\n",
+       $user->{'name'};
+
+# Show password
+print "<tr> <td nowrap><b>$text{'user_pass'}</b></td> <td>\n";
+if (!$in{'new'}) {
+       print "<input type=radio name=same value=1 checked> ",
+             "$text{'user_same'}\n";
+       print "<input type=radio name=same value=0> ",
+             "$text{'user_change'}\n";
+       }
+print "<input name=pass type=password size=20></td> </tr>\n";
+
+# Show enabled flag
+print "<tr> <td nowrap><b>$text{'user_enabled'}</b></td> <td>\n";
+printf "<input type=radio name=enabled value=1 %s> %s\n",
+       $user->{'pass'} =~ /^\*LK\*/ ? "" : "checked", $text{'yes'};
+printf "<input type=radio name=enabled value=0 %s> %s</td> </tr>\n",
+       $user->{'pass'} =~ /^\*LK\*/ ? "checked" : "", $text{'no'};
+
+# Show allowed IPS
+print "<tr> <td valign=top nowrap><b>$acl::text{'edit_ips'}</b></td>\n";
+print "<td><table><tr>\n";
+printf "<td nowrap><input name=ipmode type=radio value=0 %s> %s<br>\n",
+       $user->{'allow'} || $user->{'deny'} ? '' : 'checked',
+       $acl::text{'edit_all'};
+printf "<input name=ipmode type=radio value=1 %s> %s<br>\n",
+       $user->{'allow'} ? 'checked' : '', $acl::text{'edit_allow'};
+printf "<input name=ipmode type=radio value=2 %s> %s</td> <td>\n",
+       $user->{'deny'} ? 'checked' : '', $acl::text{'edit_deny'};
+print "<textarea name=ips rows=4 cols=30>",
+      join("\n", split(/\s+/, $user->{'allow'} ? $user->{'allow'}
+                                            : $user->{'deny'})),
+      "</textarea></td>\n";
+print "</tr></table></td> </tr>\n";
+
+# Show allowed modules (from list for *this* user)
+print "<tr> <td valign=top nowrap><b>$text{'user_mods'}</b></td>\n";
+&read_acl(\%acl);
+@mymods = grep { $acl{$base_remote_user,$_->{'dir'}} } &get_all_module_infos();
+print "<td><select name=mods size=5 multiple>\n";
+foreach $m (sort { $a->{'desc'} cmp $b->{'desc'} } @mymods) {
+       printf "<option value=%s %s>%s\n",
+               $m->{'dir'}, $gotmods{$m->{'dir'}} ? "selected" : "",
+               $m->{'desc'};
+       }
+print "</select></td> </tr>\n";
+
+# Show access control
+print "<tr> <td colspan=2><hr></td> </tr>\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 "</table></td></tr></table>\n";
+if ($in{'new'}) {
+       print "<input type=submit value='$text{'create'}'>\n";
+       }
+else {
+       print "<input type=submit value='$text{'save'}'>\n";
+       print "<input type=submit name=delete value='$text{'delete'}'>\n";
+       }
+print "</form>\n";
+&can_edit_disable("users");
+
+print "<hr>\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 (executable)
index 0000000..94f24a8
--- /dev/null
@@ -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 (file)
index 0000000..e381eb6
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 (file)
index 0000000..fa62d28
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 (file)
index 0000000..444c546
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 (file)
index 0000000..928eb63
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 (file)
index 0000000..7eebfc9
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 (file)
index 0000000..20cc7fb
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 (executable)
index 0000000..8ee1b16
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 (file)
index 0000000..73025ba
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 (file)
index 0000000..1a77764
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 (file)
index 0000000..c07cdca
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 (file)
index 0000000..5366851
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 (file)
index 0000000..1523c90
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 (file)
index 0000000..bfe181e
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 (file)
index 0000000..7eebfc9
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 (file)
index 0000000..b2d7f3b
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 (file)
index 0000000..b2d7f3b
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 (file)
index 0000000..84b41c1
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 (file)
index 0000000..2202971
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 (file)
index 0000000..f723f75
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 (file)
index 0000000..35d5f42
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 (file)
index 0000000..3a96e4e
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 (file)
index 0000000..650a153
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 (file)
index 0000000..83f72ea
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 (file)
index 0000000..ee68f9c
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 (file)
index 0000000..a858bad
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 (file)
index 0000000..14200fa
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 (file)
index 0000000..eff79f6
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 (file)
index 0000000..843d390
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 (file)
index 0000000..23ff2fe
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 (file)
index 0000000..1952c6e
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 (executable)
index 0000000..9e44a30
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<p>",&text('import_done3', scalar(@newgroups)),"<p>\n";
+
+print "<hr>\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 (executable)
index 0000000..5f6114f
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<p>",&text('import_done1', scalar(@newrules)),"<p>\n";
+
+print "<hr>\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 (executable)
index 0000000..d9871b8
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<p>",&text('import_done2', scalar(@newservs)),"<p>\n";
+
+print "<hr>\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 (executable)
index 0000000..571b6e5
--- /dev/null
@@ -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 "<hr>\n";
+
+print "<p>",&text('import_done4', scalar(@newtimes)),"<p>\n";
+
+print "<hr>\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 (executable)
index 0000000..328d187
--- /dev/null
@@ -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 "<hr>\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 "<hr>\n";
+       }
+
+print "<table width=100%>\n";
+
+if (&can_edit("apply")) {
+       # Apply button
+       print "<form action=apply.cgi><tr>\n";
+       print "<td><input type=submit value='$text{'index_apply'}'></td>\n";
+       print "<td>$text{'index_applydesc'}</td>\n";
+       print "</tr></form>\n";
+       }
+
+if (&can_edit("bootup")) {
+       &foreign_require("init", "init-lib.pl");
+       $atboot = &init::action_status("itsecur-firewall") == 2;
+
+       # At-boot button
+       print "<tr><form action=bootup.cgi>\n";
+       print "<td nowrap><input type=submit value='$text{'index_bootup'}'>\n";
+       printf "<input type=radio name=boot value=1 %s> %s\n",
+               $atboot ? "checked" : "", $text{'yes'};
+       printf "<input type=radio name=boot value=0 %s> %s\n",
+               $atboot ? "" : "checked", $text{'no'};
+       print "</td> <td>$text{'index_bootupdesc'}</td>\n";
+       print "</form></tr>\n";
+       }
+
+print "</table>\n";
+
+print "<hr>\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 "<table width=100% cellpadding=5>\n";
+for($i=0; $i<@{$_[0]}; $i++) {
+       if ($i%$cols == 0) { print "<tr>\n"; }
+       print "<td width=$per% align=center valign=top>\n";
+       &generate_icon($_[2]->[$i], $_[1]->[$i], $_[0]->[$i],
+                      ref($_[4]) ? $_[4]->[$i] : $_[4], $_[5], $_[6]);
+       print "</td>\n";
+        if ($i%$cols == $cols-1) { print "</tr>\n"; }
+        }
+while($i++%$cols) { print "<td width=$per%></td>\n"; $need_tr++; }
+print "</tr>\n" if ($need_tr);
+print "</table>\n";
+}
+
+
diff --git a/itsecur-firewall/ipf-lib.pl b/itsecur-firewall/ipf-lib.pl
new file mode 100644 (file)
index 0000000..7721f96
--- /dev/null
@@ -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 "<pre>".`cat $script_file`."</pre>\n";
+local $out = `cd /; $script_file 2>&1 </dev/null`;
+if ($?) {
+       return "IPF script output : <pre>$out</pre>";
+       }
+
+# Run the NAT config
+$out = `cd /; ipnat -C >/dev/null ; ipnat -f $nat_conf 2>&1 </dev/null`;
+if ($? || $out) {
+       return "ipnat command output : <pre>$out</pre>";
+       }
+
+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 (file)
index 0000000..4376cb7
--- /dev/null
@@ -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(<STATICS>) {
+        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(<STATICS>) {
+        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(<STATICS>) {
+               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(<STATICS>) {
+               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(<STATICS>) {
+        print SAVE "$_";
+        }
+close(STATICS);
+# Add rules
+
+# Load POSTmangle
+open(STATICS, $postmangle);
+while(<STATICS>) {
+        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 : <pre>$out</pre>";
+       }
+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 (file)
index 0000000..6eb506b
--- /dev/null
@@ -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(<GROUPS>) {
+       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(<SERVS>) {
+               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(<RULES>) {
+       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 "<font color=#0000ff>$1</font>";
+       }
+elsif ($_[0] =~ /^\!\@(.*)$/) {
+       # Negated host group
+       return "<font color=#0000ff>".&text('not', "$1")."</font>";
+       }
+elsif ($_[0] =~ /^\%(.*)$/) {
+       # Interface
+       return "<font color=#C0C0C0>".&text('iface', "$1")."</font>";
+       }
+elsif ($_[0] =~ /^\!\%(.*)$/) {
+       # Negated interface
+       return "<font color=#C0C0C0>".&text('iface_not', "$1")."</font>";
+       }
+elsif ($_[0] eq '*') {
+       # Anywhere
+       return $text{'anywhere'};
+       }
+elsif ($_[0] eq '!*') {
+       # Nowhere
+       return $text{'nowhere'};
+       }
+elsif ($_[0] =~ /^\!(.*\/.*)$/) {
+       # Negated network address
+       return &text('not', "<tt><font color=#008800>$1</font></tt>");
+       }
+elsif ($_[0] =~ /^\!([0-9\.]+)\-([0-9]+)$/) {
+       # Negated address range
+       return &text('not', "<tt><font color=#ffff00>$1-$2</font></tt>");
+       }
+elsif ($_[0] =~ /^\!(.*)$/) {
+       # Negated hostname or IP
+       return &text('not', "<tt>$1</tt>");
+       }
+elsif ($_[0] =~ /^(.*\/.*)$/) {
+       # Network address
+       return "<tt><font color=#008800>$_[0]</font></tt>";
+       }
+elsif ($_[0] =~ /^([0-9\.]+)\-([0-9]+)$/) {
+       # Address range
+       return "<tt><font color=#ffff00>$1-$2</font></tt>";
+       }
+else {
+       # Hostname or IP
+       return "<tt>$_[0]</tt>";
+       }
+}
+
+# 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, "<a href='edit_group.cgi?name=$1&from=$_[1]'>".
+                         &group_name($g, $_[2])."</a>");
+               }
+       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] ? "<select name=$_[0] size=5 multiple>"
+                 : "<select name=$_[0]>\n";
+if ($_[2]) {
+       $rv .= sprintf "<option value='' %s>%s\n",
+               $_[1] ? "" : "selected", $_[2] == 2 ? $text{'other'} : "&nbsp;";
+       }
+local $g;
+local %vals = map { $_, 1 } split(/\s+/, $_[1]);
+foreach $g (@groups) {
+       $rv .= sprintf "<option value=%s %s>%s\n",
+               $g->{'name'}, $vals{$g->{'name'}} ? "selected" : "",
+               $g->{'name'};
+       }
+$rv .= "</select>\n";
+return $rv;
+}
+
+# service_input(name, value, [blankoption], [multiple], [norange])
+sub service_input
+{
+local @servs = &list_services();
+local %got = map { $_, 1 } split(/,/, $_[1]);
+local $rv = $_[3] ? "<select name=$_[0] size=5 multiple>"
+                 : "<select name=$_[0]>\n";
+if ($_[2]) {
+       $rv .= sprintf "<option value='' %s>%s\n",
+               $_[1] ? "" : "selected", $_[2] == 2 ? $text{'other'} : "&nbsp;";
+       }
+local $s;
+foreach $s (@servs) {
+       local $desc;
+       local @up = &unique(@{$s->{'protos'}});
+       local $i;
+       if (@up == 1) {
+               $desc = uc($up[0])." ".join(", ", @{$s->{'ports'}});
+               }
+       else {
+               for($i=0; $i<@{$s->{'protos'}}; $i++) {
+                       $desc .= ", " if ($desc);
+                       $desc .= uc($s->{'protos'}->[$i])."/".  
+                                $s->{'ports'}->[$i];
+                       }
+               }
+       for($i=0; $i<@{$s->{'others'}}; $i++) {
+               $desc .= ", " if ($desc);
+               $desc .= $s->{'others'}->[$i];
+               }
+       $rv .= sprintf "<option value=%s %s>%s%s\n",
+               $s->{'name'}, $got{$s->{'name'}} ? "selected" : "",
+               $s->{'name'}, $_[4] ? "" : " ($desc)";
+       }
+$rv .= "</select>\n";
+return $rv;
+}
+
+# action_input(name, value, [select-mode])
+sub action_input
+{
+local $rv;
+local $a;
+if ($_[2]) {
+       $rv .= "<select name=$_[0]>\n";
+       foreach $a (@actions) {
+               $rv .= sprintf "<option value=%s %s>%s\n",
+                       $a, $_[1] eq $a ? "selected" : "",
+                       $text{"rule_".$a};
+               }
+       $rv .= "</select>\n";
+       }
+else {
+       foreach $a (@actions) {
+               $rv .= sprintf "<input type=radio name=%s value=%s %s>%s\n",
+                       $_[0], $a, $_[1] eq $a ? "checked" : "",
+                       $text{"rule_".$a};
+               }
+       }
+return $rv;
+}
+
+
+# protocol_input(name, value)
+sub protocol_input
+{
+local @protos = ( 'tcp', 'udp', 'icmp', 'ip' );
+#open(PROTOS, "/etc/protocols");
+#while(<PROTOS>) {
+#        s/\r|\n//g;
+#        s/#.*$//;
+#        push(@protos, $1) if (/^(\S+)\s+(\d+)/);
+#        }
+#close(PROTOS);
+local $p;
+local $rv = "<select name=$_[0]>\n";
+$rv .= sprintf "<option value='' %s>&nbsp;\n",
+               $_[1] eq '' ? "selected" : "";
+foreach $p (&unique(@protos)) {
+        $rv .= sprintf "<option value='%s' %s>%s\n",
+                        $p, $_[1] eq $p && $p ? "selected" : "",
+                        uc($p) || "-------";
+        }
+$rv .= "</select>\n";
+return $rv;
+}
+
+sub valid_host
+{
+
+if (&check_ipaddress($_[0])) {
+       return 1;
+       }
+elsif (gethostbyname($_[0])) {
+       return 2;
+       }
+elsif (&check_netaddress($_[0])) {
+       #$_[0] =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/) {
+       return 3;
+       }
+elsif ($_[0] =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)\-(\d+)$/) {
+       return 4;
+       }
+else {
+       return 0;
+       }
+}
+
+# iface_input(name, value, [realonly], [nother], [nonemode])
+sub iface_input
+{
+local @ifaces;
+if (&foreign_check("net")) {
+       &foreign_require("net", "net-lib.pl");
+       local $i;
+       foreach $i (&net::active_interfaces(), &net::boot_interfaces()) {
+               push(@ifaces, $i->{'fullname'})
+                       if (!$_[2] || $i->{'virtual'} eq '');
+               }
+       @ifaces = &unique(@ifaces);
+       }
+if (@ifaces) {
+       local $rv = "<select name=$_[0]>\n";
+       local ($i, $found);
+       if ($_[4]) {
+               $rv .= sprintf "<option value='' %s>%s\n",
+                       $_[1] eq "" ? "selected" : "", "&lt;None&gt;";
+               $found++ if ($_[1] eq "");
+               }
+       foreach $i (@ifaces) {
+               $rv .= sprintf "<option value=%s %s>%s\n",
+                       $i, $_[1] eq $i ? "selected" : "", $i;
+               $found++ if ($_[1] eq $i);
+               }
+       if ($_[3]) {
+               $rv .= "<option value=$_[1] selected>$_[1]\n" if (!$found && $_[1]);
+               }
+       else {
+               $rv .= sprintf "<option value='' %s> %s\n",
+                               !$found && $_[1] ? "selected" : "", $text{'rule_oifc'};
+               $rv .= "</select>\n";
+               $rv .= sprintf "<input name=$_[0]_other size=6 value='%s'>\n",
+                               !$found ? $_[1] : "";
+               }
+       return $rv;
+       }
+else {
+       return "<input name=$_[0] size=6 value='$_[1]'>";
+       }
+}
+
+# time_input(name, [value])
+sub time_input
+{
+local @times = &list_times();
+return undef if (!@times);
+local $rv = "<select name=$_[0]>\n";
+local $t;
+foreach $t (@times) {
+       $rv .= sprintf "<option value=%s %s>%s\n",
+               $t->{'name'}, $t->{'name'} eq $_[1] ? "selected" : "",
+               $t->{'name'};
+       }
+$rv .= "</select>\n";
+return $rv;
+}
+
+# get_nat([file])
+sub get_nat
+{
+local ($iface, @nets, @maps);
+open(NAT, $_[0] || $nat_file) || return ( );
+chop($iface = <NAT>);
+while(<NAT>) {
+       s/\r|\n//g;
+       if (/^(\S+)$/) {
+               push(@nets, $_);
+               }
+       elsif (/^(\S+)\t+(\S+)\t+(\S+)$/) {
+               push(@maps, [ $1, $2, $3 ]);
+               }
+       elsif (/^(\S+)\t+(\S+)$/) {
+               push(@maps, [ $1, $2 ]);
+               }
+       }
+close(NAT);
+return ( $iface, @nets, @maps );
+}
+
+# save_nat(iface, net, ..)
+sub save_nat
+{
+open(NAT, ">$nat_file");
+print NAT shift(@_),"\n";
+local $n;
+foreach $n (@_) {
+        if (ref($n)) {
+                print NAT join("\t", @$n),"\n";
+                }
+        else {
+                print NAT $n,"\n";
+                }
+        }
+close(NAT);
+}
+
+sub save_nat2
+{
+open(NAT, ">$nat2_file");
+print NAT shift(@_),"\n";
+local $n;
+foreach $n (@_) {
+        if (ref($n)) {
+                print NAT join("\t", @$n),"\n";
+                }
+        else {
+                print NAT $n,"\n";
+                }
+        }
+close(NAT);
+}
+
+
+# get_pat([file])
+sub get_pat
+{
+local ($defiface, @forwards);
+open(PAT, $_[0] || $pat_file) || return ( );
+chop($defiface = <PAT>);
+while(<PAT>) {
+       s/\r|\n//g;
+       if (/^(\S+)\t+(\S+)\t+(\S+)$/) {
+               push(@forwards, { 'service' => $1,
+                                 'host' => $2,
+                                 'iface' => $3 });
+               }
+       elsif (/^(\S+)\t+(\S+)$/) {
+               push(@forwards, { 'service' => $1,
+                                 'host' => $2,
+                                 'iface' => $defiface });
+               }
+       }
+close(PAT);
+return @forwards;
+}
+
+# save_pat(forward, ...)
+sub save_pat
+{
+open(PAT, ">$pat_file");
+print PAT (@_ ? $_[0]->{'iface'} : ""),"\n";
+local $f;
+foreach $f (@_) {
+       if ($f->{'iface'}) {
+               print PAT "$f->{'service'}\t$f->{'host'}\t$f->{'iface'}\n";
+               }
+       else {
+               print PAT "$f->{'service'}\t$f->{'host'}\n";
+               }
+       }
+close(PAT);
+}
+
+# get_spoof([file])
+sub get_spoof
+{
+local ($iface, @nets);
+open(PAT, $_[0] || $spoof_file) || return ( );
+chop($iface = <PAT>);
+while(<PAT>) {
+       s/\r|\n//g;
+       if (/^(\S+)$/) {
+               push(@nets, $_);
+               }
+       }
+close(PAT);
+return ( $iface, @nets );
+}
+
+# save_spoof(iface, net, ...)
+sub save_spoof
+{
+open(PAT, ">$spoof_file");
+print PAT shift(@_),"\n";
+local $s;
+foreach $s (@_) {
+       print PAT "$s\n";
+       }
+close(PAT);
+}
+
+# get_syn([file])
+sub get_syn
+{
+local ($flood, $spoof, $fin);
+open(SYN, $_[0] || $syn_file) || return ( );
+chop($flood = <SYN>);
+chop($spoof = <SYN>);
+chop($fin = <SYN>);
+close(SYN);
+return ($flood, $spoof, $fin);
+}
+
+# save_syn(flood, spoof, fin)
+sub save_syn
+{
+open(SYN, ">$syn_file");
+print SYN int($_[0]),"\n";
+print SYN int($_[1]),"\n";
+print SYN int($_[2]),"\n";
+close(SYN);
+}
+
+# list_times([file])
+# Returns a list of all time ranges
+sub list_times
+{
+local @rv;
+open(TIMES, $_[0] || $times_file);
+while(<TIMES>) {
+       s/\r|\n//g;
+       local @t = split(/\t/, $_);
+       if (@t >= 3) {
+               local $time = { 'index' => scalar(@rv),
+                               'name' => $t[0],
+                               'hours' => $t[1],
+                               'days' => $t[2] };
+               push(@rv, $time);
+               }
+       }
+close(TIMES);
+return @rv;
+}
+
+# save_times(time, ...)
+# Updates the time ranges list
+sub save_times
+{
+open(TIMES, ">$times_file");
+local $t;
+foreach $t (@_) {
+       print TIMES $t->{'name'},"\t",
+                   $t->{'hours'},"\t",
+                   $t->{'days'},"\n";
+       }
+close(TIMES);
+}
+
+# activate_interface(base, ip)
+sub activate_interface
+{
+&foreign_require("net", "net-lib.pl");
+local @active = &net::active_interfaces();
+local ($base) = grep { $_->{'fullname'} eq $_[0] } @active;
+local ($already) = grep { $_->{'address'} eq $_[1] } @active;
+if ($base && !$already) {
+       # Work out an interface number
+       local $vmax = 0;
+       foreach $a (@active) {
+               $vmax = $a->{'virtual'}
+                       if ($a->{'name'} eq $base->{'name'} &&
+                           $a->{'virtual'} > $vmax);
+               }
+
+       # Activate now
+       $virt = { 'address' => $_[1],
+                 'netmask' => $base->{'netmask'},
+                 'broadcast' => $base->{'broadcast'},
+                 'name' => $base->{'name'},
+                 'virtual' => $vmax+1,
+                 'up' => 1 };
+       $virt->{'fullname'} = $virt->{'name'}.":".$virt->{'virtual'};
+       &net::activate_interface($virt);
+
+       # Save for later
+       open(ACTIVE, ">>$active_interfaces");
+       print ACTIVE "$virt->{'fullname'}\t$virt->{'address'}\n";
+       close(ACTIVE);
+       }
+}
+
+# deactivate_all_interfaces()
+# Shuts down all interfaces activated by the above function
+sub deactivate_all_interfaces
+{
+&foreign_require("net", "net-lib.pl");
+open(ACTIVE, $active_interfaces);
+while(<ACTIVE>) {
+       if (/^(\S+)\s+(\S+)/) {
+               local $addr = $2;
+               local @active = &net::active_interfaces();
+               local ($virt) = grep { $_->{'address'} eq $addr } @active;
+               if ($virt && $virt->{'virtual'} ne '') {
+                       &net::deactivate_interface($virt);
+                       }
+               }
+       }
+close(ACTIVE);
+unlink($active_interfaces);
+}
+
+sub apply_button
+{
+if (&can_edit("apply")) {
+       return "<a href='apply.cgi?return=1'>$text{'apply_button'}</a>";
+       }
+else {
+       return undef;
+       }
+}
+
+# expand_hosts(names, &groups)
+# Give a list of host or group names, expands them to hosts
+sub expand_hosts
+{
+local ($e, @rv);
+local %groups = map { $_->{'name'}, $_ } @{$_[1]};
+foreach $e (split(/\s+/, $_[0])) {
+       if ($e =~ /^\@(.*)$/) {
+               # Expand to all group members
+               local $group = $groups{$1};
+               foreach $m (@{$group->{'members'}}) {
+                       push(@rv, &expand_hosts($m, $_[1]));
+                       }
+               }
+       elsif ($e =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)\-(\d+)$/) {
+               # Expand to all IPs in range
+               push(@rv, map { "$1.$2.$3.$_" } ($4 .. $5) );
+               }
+       else {
+               # Just a single IP, host or network
+               push(@rv, $e);
+               }
+       }
+return @rv;
+}
+
+# can_use(feature)
+sub can_use
+{
+return 1 if ($read_access{'*'} || $edit_access{'*'});
+return $read_access{$_[0]} || $edit_access{$_[0]};
+}
+
+# can_edit(feature)
+sub can_edit
+{
+return 0 if (!&can_use($_[0]));
+return $edit_access{'*'} || $edit_access{$_[0]};
+}
+
+# can_use_error(feature)
+sub can_use_error
+{
+&can_use($_[0]) || &error($text{$_[0]."_ecannot"} ||
+                         &text('ecannot', $text{$_[0]."_title"}));
+}
+
+# can_edit_error(feature)
+sub can_edit_error
+{
+&can_edit($_[0]) || &error($text{$_[0]."_ecannot"} ||
+                         &text('ecannot', $text{$_[0]."_title"}));
+}
+
+# can_edit_disable(feature)
+sub can_edit_disable
+{
+if (!&can_edit($_[0])) {
+       print "<script>\n";
+       print "l = document.forms[0].elements;\n";
+       print "for(i=0; i<l.length; i++) {\n";
+       print "    if (l[i].name != \"burn\" && l[i].name != \"test\" &&\n";
+       print "        l[i].type != \"hidden\") {\n";
+       print "        l[i].disabled = true;\n";
+       print "    }\n";
+       print "}\n";
+       print "</script>\n";
+       }
+}
+
+# protocol_name(proto, port)
+sub protocol_name
+{
+local $pr = uc($_[0]);
+local $pt = $_[1];
+if ($pr eq "TCP") {
+       return "<font color=#0000ff>$pr/$pt</font>";
+       }
+elsif ($pr eq "UDP") {
+       return "<font color=#008800>$pr/$pt</font>";
+       }
+elsif ($pr eq "ICMP") {
+       return "<font color=#f00000>$pr/$pt</font>";
+       }
+else {
+       return "$pr/$pt";
+       }
+}
+
+# protocol_names(comma-list, [&services])
+sub protocol_names
+{
+if ($_[0] eq "*") {
+       return $text{'rule_any'};
+       }
+else {
+       local %sn = map { $_->{'name'}, $_ }
+                       ( $_[1] ? @{$_[1]} : &list_services() );
+       local $sn;
+       local @rv;
+       local ($editServO,$editServC);
+       foreach $sn (split(/,/, $_[0])) {
+               local $serv = $sn{$sn};
+               if (!$serv->{'standard'}){
+                               $editServO="<a href='edit_service.cgi?name=".$serv->{'name'}."'>";
+                               $editServC="</a>";                              
+                       } else {
+                               $editServO="";
+                               $editServC="";                                                  
+                       }
+               local $pr = @{$serv->{'protos'}} == 1 ? uc($serv->{'protos'}->[0]) : undef;
+               if ($pr eq "TCP") {
+                       push(@rv, "$editServO<font color=#0000ff>$sn</font>$editServC");
+                       }
+               elsif ($pr eq "UDP") {
+                       push(@rv, "$editServO<font color=#008800>$sn</font>$editServC");
+                       }
+               elsif ($pr eq "ICMP") {
+                       push(@rv, "$editServO<font color=#f00000>$sn</font>$editServC");
+                       }
+               else {
+                       push(@rv, "$editServO $sn $editServC");
+                       }
+               }
+       return join(", ", @rv);
+       }
+}
+
+# my_address_in(address/network)
+# Returns this system's IP address in some network
+sub my_address_in
+{
+local $net = $_[0];
+$net =~ s/^\!\s+//;
+if ($net =~ /[a-z]/i) {
+       $net = &to_ipaddress($net);
+       }
+$net =~ s/^(\d+\.\d+\.\d+).*$/$1/;
+&foreign_require("net", "net-lib.pl");
+local @ifaces = &net::active_interfaces();
+local $i;
+local $primary;
+foreach $i (@ifaces) {
+       if ($i->{'up'}) {
+               if (!$primary && $i->{'fullname'} !~ /^lo/) {
+                       $primary = $i->{'address'};
+                       }
+               local $addr = $i->{'address'};
+               $addr =~ s/^(\d+\.\d+\.\d+).*$/$1/;
+               if ($addr eq $net) {
+                       return $i->{'address'};
+                       }
+               }
+       }
+return $primary;
+}
+
+sub has_ipsec
+{
+return &foreign_installed("ipsec", 1);
+}
+
+# backup_firewall(&what, file, [password])
+# Backs up the firewall to some file
+sub backup_firewall
+{
+local ($mode, @dest) = &parse_backup_dest($_[1]);
+local $file = $mode == 1 ? $dest[0] : &tempname();
+local $ipsec_tmp = "$module_config_directory/ipsec.conf";
+local $secrets_tmp = "$module_config_directory/ipsec.secrets";
+local $users_tmp = "$module_config_directory/miniserv.users";
+local $acl_tmp = "$module_config_directory/webmin.acl";
+local $w;
+local (@files, @delfiles);
+foreach $w (@{$_[0]}) {
+       if ($w eq "ipsec") {
+               # Copy the ipsec.conf files
+               if (&has_ipsec()) {
+                       system("cp $ipsec::config{'file'} $ipsec_tmp");
+                       system("cp $ipsec::config{'secrets'} $secrets_tmp");
+                       push(@delfiles, "ipsec.conf", "ipsec.secrets");
+                       }
+               }
+       elsif ($w eq "users") {
+               # Copy all Webmin users
+               opendir(DIR, $module_config_directory);
+               push(@files, grep { /\.acl$/ } readdir(DIR));
+               closedir(DIR);
+               system("cp $config_directory/miniserv.users $users_tmp");
+               system("cp $config_directory/webmin.acl $acl_tmp");
+               push(@delfiles, "miniserv.users", "webmin.acl");
+               }
+       else {
+               push(@files, $w) if (-r "$module_config_directory/$w");
+               }
+       }
+push(@files, @delfiles);
+local $what = join(" ", @files);
+return $text{'backup_ewhat2'} if (!$what);
+local $pass = $_[2] ? "-P '$_[2]'" : "";
+local $out = &backquote_logged("(cd $module_config_directory ; zip -r $pass '$file' $what) 2>&1");
+return "<pre>$out</pre>" if ($?);
+unlink(map { "$module_config_directory/$_" } @delfiles);
+
+# Send to destination
+if ($mode == 2) {
+       # FTP somewhere
+       local $err;
+       &ftp_upload($dest[2], $dest[3], $file, \$err, undef, $dest[0], $dest[1]);
+       unlink($file);
+       return $err if ($err);
+       }
+elsif ($mode == 3) {
+       # Email somewhere
+       $data = `cat $file`;
+       unlink($file);
+       $host = &get_system_hostname();
+       $body = "The backup of the firewall configuration on $host is attached to this email.\n";
+       local $mail = { 'headers' =>
+                       [ [ 'From', $config{'from'} || "webmin\@$host" ],
+                         [ 'To', $dest[0] ],
+                         [ 'Subject', "Firewall backup" ] ],
+                       'attach' =>
+                       [ { 'headers' => [ [ 'Content-type', 'text/plain' ] ],
+                           'data' => $body },
+                         { 'headers' => [ [ 'Content-type', 'application/zip' ],
+                                          [ 'Content-Transfer-Encoding', 'base64' ] ],
+                           'data' => $data } ] };
+       $main::errors_must_die = 1;
+       if (&foreign_check("mailboxes")) {
+               &foreign_require("mailboxes", "mailboxes-lib.pl");
+               eval { &mailboxes::send_mail($mail); };
+               }
+       else {
+               &foreign_require("sendmail", "sendmail-lib.pl");
+               &foreign_require("sendmail", "boxes-lib.pl");
+               eval { &sendmail::send_mail($mail); };
+               }
+       return $@ if ($@);
+       }
+
+return undef;
+}
+
+sub check_zip
+{
+&has_command("zip") && &has_command("unzip") ||
+       &error($text{'backup_ezipcmd'});
+}
+
+sub find_backup_job
+{
+&foreign_require("cron", "cron-lib.pl");
+local @jobs = &cron::list_cron_jobs();
+local ($job) = grep { $_->{'user'} eq 'root' &&
+                     $_->{'command'} eq $cron_cmd } @jobs;
+return $job;
+}
+
+sub parse_backup_dest
+{
+if ($_[0] =~ /^mailto:(.*)/) {
+       return (3, $1);
+       }
+elsif ($_[0] =~ /^ftp:\/\/([^:]*):([^@]*)@([^\/]+)(\/.*)$/) {
+       return (2, $1, $2, $3, $4);
+       }
+elsif ($_[0] =~ /^\//) {
+       return (1, $_[0]);
+       }
+else {
+       return (0);
+       }
+}
+
+# ftp_upload(host, file, srcfile, [&error], [&callback], [user, pass])
+# Download data from a local file to an FTP site
+sub ftp_upload
+{
+local($buf, @n);
+local $cbfunc = $_[4];
+
+$download_timed_out = undef;
+local $SIG{ALRM} = "download_timeout";
+alarm(60);
+
+# connect to host and login
+&open_socket($_[0], 21, "SOCK", $_[3]) || return 0;
+alarm(0);
+if ($download_timed_out) {
+       if ($_[3]) { ${$_[3]} = $download_timed_out; return 0; }
+       else { &error($download_timed_out); }
+       }
+&ftp_command("", 2, $_[3]) || return 0;
+if ($_[5]) {
+       # Login as supplied user
+       local @urv = &ftp_command("USER $_[5]", [ 2, 3 ], $_[3]);
+       @urv || return 0;
+       if (int($urv[1]/100) == 3) {
+               &ftp_command("PASS $_[6]", 2, $_[3]) || return 0;
+               }
+       }
+else {
+       # Login as anonymous
+       local @urv = &ftp_command("USER anonymous", [ 2, 3 ], $_[3]);
+       @urv || return 0;
+       if (int($urv[1]/100) == 3) {
+               &ftp_command("PASS root\@".&get_system_hostname(), 2,
+                            $_[3]) || return 0;
+               }
+       }
+&$cbfunc(1, 0) if ($cbfunc);
+
+&ftp_command("TYPE I", 2, $_[3]) || return 0;
+
+# get the file size and tell the callback
+local @st = stat($_[2]);
+if ($cbfunc) {
+       &$cbfunc(2, $st[7]);
+       }
+
+# send the file
+local $pasv = &ftp_command("PASV", 2, $_[3]);
+defined($pasv) || return 0;
+$pasv =~ /\(([0-9,]+)\)/;
+@n = split(/,/ , $1);
+&open_socket("$n[0].$n[1].$n[2].$n[3]", $n[4]*256 + $n[5], "CON", $_[3]) || return 0;
+&ftp_command("STOR $_[1]", 1, $_[3]) || return 0;
+
+# transfer data
+local $got;
+open(PFILE, $_[2]);
+while(read(PFILE, $buf, 1024) > 0) {
+       print CON $buf;
+       $got += length($buf);
+       &$cbfunc(3, $got) if ($cbfunc);
+       }
+close(PFILE);
+close(CON);
+if ($got != $st[7]) {
+       if ($_[3]) { ${$_[3]} = "Upload incomplete"; return 0; }
+       else { &error("Upload incomplete"); }
+       }
+&$cbfunc(4) if ($cbfunc);
+
+# finish off..
+&ftp_command("", 2, $_[3]) || return 0;
+&ftp_command("QUIT", 2, $_[3]) || return 0;
+close(SOCK);
+
+return 1;
+}
+
+# lock_itsecur_files()
+# Lock all firewall config files
+sub lock_itsecur_files
+{
+local $f;
+foreach $f (@config_files) {
+       &lock_file($f);
+       }
+}
+
+# unlock_itsecur_files()
+# Unlock all firewall config files
+sub unlock_itsecur_files
+{
+local $f;
+foreach $f (@config_files) {
+       &unlock_file($f);
+       }
+}
+
+sub remote_webmin_log
+{
+if ($config{'remote_log'} && !fork()) {
+       # Disconnect from TTY
+       untie(*STDIN);
+       untie(*STDOUT);
+       untie(*STDERR);
+       close(STDIN);
+       close(STDOUT);
+       close(STDERR);
+
+       # Send log to remote host
+       &remote_foreign_require($config{'remote_log'}, $module_name,
+                               "itsecur-lib.pl");
+       local $d;
+       foreach $d (@main::locked_file_diff) {
+               &remote_foreign_call($config{'remote_log'}, $module_name,
+                                    "additional_log", $d->{'type'},
+                                    $d->{'object'}, $d->{'data'});
+               }
+       local $script_name = $0 =~ /([^\/]+)$/ ? $1 : '';
+       &remote_foreign_call($config{'remote_log'}, $module_name,
+                            "webmin_log", @_[0..3], $module_name,
+                            &get_system_hostname(),
+                            $script_name,
+                            $ENV{'REMOTE_HOST'});
+
+       exit(0);
+       }
+&webmin_log(@_);
+}
+
+# automatic_backup()
+# If a change has been made and an automatic backup directory set, save the
+# module's configuration
+sub automatic_backup
+{
+return if (!$config{'auto_dir'} || !-d $config{'auto_dir'});
+
+# Backup to a temp file
+local $temp = &tempname();
+local $err = &backup_firewall(\@backup_opts, $temp, undef);
+if ($err) {
+       unlink($temp);
+       return 0;
+       }
+
+# Make sure this backup is actually different from the last
+local $linkfile = "$config{'auto_dir'}/latest.zip";
+if (-r $linkfile) {
+       local $out = `diff '$config{'auto_dir'}/latest.zip' '$temp' 2>&1`;
+       if ($? == 0) {
+               # No change!
+               unlink($temp);
+               return 0;
+               }
+       }
+
+# Copy to directory, and update latest link
+use POSIX;
+local $newfile = strftime "$config{'auto_dir'}/firewall.%Y-%m-%d-%H:%M:%S.zip",
+                       localtime(time());
+system("mv '$temp' '$newfile'");
+unlink($linkfile);
+symlink($newfile, $linkfile);
+
+return 1;
+}
+
+# parse_all_logs([base-only])
+# Returns a list of all log structures, newest first
+sub parse_all_logs
+{
+local $baselog = $config{'log'} || &get_log_file();
+local @rv;
+foreach $log ($config{'all_files'} && !$_[0] ? &all_log_files($baselog)
+                                            : ($baselog)) {
+       if ($log =~ /\.gz$/i) {
+               open(LOG, "gunzip -c ".quotemeta($log)." |");
+               }
+       elsif ($log =~ /\.Z$/i) {
+               open(LOG, "uncompress -c ".quotemeta($log)." |");
+               }
+       else {
+               open(LOG, $log);
+               }
+       while(<LOG>) {
+               local $info = &parse_log_line($_);
+               push(@rv, $info) if ($info);
+               }
+       close(LOG);
+       }
+return reverse(@rv);
+}
+
+# all_log_files(file)
+sub all_log_files
+{
+$_[0] =~ /^(.*)\/([^\/]+)$/;
+local $dir = $1;
+local $base = $2;
+local ($f, @rv);
+opendir(DIR, $dir);
+foreach $f (readdir(DIR)) {
+       if ($f =~ /^\Q$base\E/ && -f "$dir/$f") {
+               push(@rv, "$dir/$f");
+               }
+       }
+closedir(DIR);
+return @rv;
+}
+
+@search_fields = ("src", "dst", "dst_iface", "proto", "src_port", "dst_port",
+                 "first", "last", "action", "rule");
+
+# filter_logs(&logs, &in, [&searchvars])
+sub filter_logs
+{
+local @logs = @{$_[0]};
+local %in = %{$_[1]};
+local $f;
+local @servs = &list_services();
+local @groups = &list_groups();
+local %servs = map { $_->{'name'}, $_ } @servs;
+foreach $f (@search_fields) {
+       if ($in{$f."_mode"}) {
+               # This search applies .. find all suitable match types
+               local %matches;
+               local $tm;
+               if (($f eq "src_port" || $f eq "dst_port") && $in{$f."_what"}) {
+                       # Lookup all ports and protocols
+                       local ($protos, $ports) =
+                               &combine_services($in{$f."_what"}, \%servs);
+                       local $i;
+                       for($i=0; $i<@$protos; $i++) {
+                               if ($ports->[$i] =~ /^(\d+)\-(\d+)$/) {
+                                       local $p;
+                                       foreach $p ($1 .. $2) {
+                                               $matches{lc($protos->[$i]),$p}++;
+                                               }
+                                       }
+                               else {
+                                       $matches{lc($protos->[$i]),$ports->[$i]}++;
+                                       }
+                               }
+                       }
+               elsif (($f eq "src_port" || $f eq "dst_port") && !$in{$f."_what"}) {
+                       # One specified port number
+                       $matches{$in{$f."_other"}}++;
+                       }
+               elsif (($f eq "src" || $f eq "dst") && $in{$f."_what"}) {
+                       # Lookup all hosts
+                       local @hosts = &expand_hosts(
+                               '@'.$in{$f."_what"}, \@groups);
+                       local $h;
+                       foreach $h (@hosts) {
+                               local $eh;
+                               foreach $eh (&expand_net($h)) {
+                                       $matches{$eh}++;
+                                       }
+                               }
+                       }
+               elsif (($f eq "src" || $f eq "dst") && !$in{$f."_what"}) {
+                       # One other host
+                       local $eh;
+                       foreach $eh (&expand_net($in{$f."_other"})) {
+                               $matches{$eh}++;
+                               }
+                       }
+               elsif ($f eq "first" || $f eq "last") {
+                       # A time range
+                       eval { $tm = timelocal(
+                               0, $in{$f."_min"}, $in{$f."_hour"},
+                               $in{$f."_day"}, $in{$f."_month"}-1,
+                               $in{$f."_year"}-1900); };
+                       }
+               else {
+                       $matches{lc($in{$f."_what"})}++;
+                       }
+
+               if ($f eq "first" && $tm) {
+                       # Find those after start minute
+                       @logs = grep { $_->{'time'} >= $tm } @logs;
+                       }
+               elsif ($f eq "last" && $tm) {
+                       # Find those before end minute
+                       @logs = grep { $_->{'time'} < $tm+60 } @logs;
+                       }
+               elsif ($in{$f."_mode"} == 1) {
+                       # Find matching entries
+                       @logs = grep {
+                               $matches{lc($_->{$f})} ||
+                               $matches{lc($_->{'proto'}),lc($_->{$f})} }
+                                    @logs;
+                       }
+               elsif ($in{$f."_mode"} == 2) {
+                       # Find non-matching entries
+                       @logs = grep {
+                               !($matches{lc($_->{$f})} ||
+                                 $matches{lc($_->{'proto'}),lc($_->{$f})}) }
+                                    @logs;
+                       }
+               if ($_[2]) {
+                       local $e;
+                       foreach $e ("mode", "what", "other", "day",
+                                   "month", "year") {
+                               if ($in{$f."_".$e} ne "") {
+                                       push(@{$_[2]}, $f."_".$e."=".
+                                            &urlize($in{$f."_".$e}));
+                                       }
+                               }
+                       }
+               }
+       }
+return @logs;
+}
+
+# expand_net(network)
+# Given a network address, hostname or IP address, returns a list of all
+# IP addresses it contains
+sub expand_net
+{
+if ($_[0] =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/) {
+       local @rv;
+       local $first = ($1<<24) + ($2<<16) + ($3<<8) + ($4);
+       local $last = $first + (1<<(32-$5)) - 1;
+       for($ipnum=$first; $ipnum<=$last; $ipnum++) {
+               local @ip = ( ($ipnum>>24)&0xff,
+                             ($ipnum>>16)&0xff,
+                             ($ipnum>>8)&0xff,
+                             ($ipnum)&0xff );
+               push(@rv, join(".", @ip));
+               }
+       return @rv;
+       }
+else {
+       return &to_ipaddress($_[0]);
+       }
+}
+
+# list_searches()
+# Returns a list of all saved searches
+sub list_searches
+{
+local @rv;
+opendir(DIR, $searches_dir);
+local $f;
+while($f = readdir(DIR)) {
+       if ($f ne "." && $f ne "..") {
+               local $search = &get_search($f);
+               push(@rv, $search) if ($search);
+               }
+       }
+closedir(DIR);
+return @rv;
+}
+
+sub get_search
+{
+local %search;
+if (&read_file("$searches_dir/$_[0]", \%search)) {
+       return \%search;
+       }
+else {
+       return undef;
+       }
+}
+
+# save_search(&search)
+sub save_search
+{
+mkdir($searches_dir, 0755);
+&write_file("$searches_dir/$_[0]->{'save_name'}", $_[0]);
+}
+
+# get_remote()
+# Returns the webmin servers object used for remote logging, or undef
+sub get_remote
+{
+return undef if (!$config{'remote_log'});
+&foreign_require("servers", "servers-lib.pl");
+local @servers = &servers::list_servers();
+local ($server) = grep { $_->{'host'} eq $config{'remote_log'} } @servers;
+return $server;
+}
+
+# save_remote(server, port, username, password, test, save)
+sub save_remote
+{
+local ($host, $port, $user, $pass, $test, $save) = @_;
+&foreign_require("servers", "servers-lib.pl");
+if ($host) {
+       # Enabling or updating
+       local @servers = &servers::list_servers();
+       local ($newserver) = grep { $_->{'host'} eq $host } @servers;
+       local $server = &get_remote();
+       if ($newserver && $server) {
+               if ($newserver ne $server) {
+                       # Re-name would cause clash, so delete it
+                       &servers::delete_server($newserver->{'id'});
+                       }
+               }
+       elsif ($newserver && !$server) {
+               # Re-naming server
+               $server = $newserver;
+               }
+       elsif (!$newserver && $server) {
+               # Can just stick to old server
+               }
+       else {
+               # Totally new
+               $server = { 'id' => time(),
+                           'port' => $port,
+                           'ssl' => 0,
+                           'desc' => 'Firewall logging server',
+                           'type' => 'unknown',
+                           'fast' => 0 };
+               }
+       $server->{'host'} = $host;
+       $server->{'port'} = $port;
+       $server->{'user'} = $user;
+       $server->{'pass'} = $pass;
+       &servers::save_server($server);
+       $config{'remote_log'} = $server->{'host'};
+
+       if ($test) {
+               # Try a test connection
+               &remote_error_setup(\&test_error);
+               eval {
+                       $SIG{'ALRM'} = sub { die "alarm\n" };
+                       alarm(10);
+                       &remote_foreign_require($server->{'host'}, "webmin",
+                                               "webmin-lib.pl");
+                       alarm(0);
+                       };
+               if ($@) {
+                       &error(&text('remote_econnect', $text{'remote_etimeout'}));
+                       }
+               elsif ($test_error_msg) {
+                       &error(&text('remote_econnect', $test_error_msg));
+                       }
+               }
+       }
+else {
+       # Disabling
+       delete($config{'remote_log'});
+       }
+if ($save) {
+       &lock_file($module_config_file);
+       &write_file($module_config_file, \%config);
+       &unlock_file($module_config_file);
+       }
+}
+
+sub test_error
+{
+$test_error_msg = join("", @_);
+}
+
+sub check_netaddress
+{
+return $_[0] =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/ &&
+       $1 >= 0 && $1 <= 255 &&
+       $2 >= 0 && $2 <= 255 &&
+       $3 >= 0 && $3 <= 255 &&
+       $4 >= 0 && $4 <= 255 &&
+       $5 >= 0 && $5 <= 32;
+}
+
+sub is_one_host
+{
+local @groups = &list_groups();
+local @rv=&expand_hosts($_[0], \@groups);
+return $#rv;
+}
+
+1;
+
diff --git a/itsecur-firewall/lang/en b/itsecur-firewall/lang/en
new file mode 100644 (file)
index 0000000..f984e87
--- /dev/null
@@ -0,0 +1,490 @@
+index_title=Simple Firewall Creator
+index_version=Version $1
+index_iptables=Using iptables
+index_ipf=Using ipf
+index_apply=Apply Configuration
+index_applydesc=Click this button to apply the current firewall configuration, using the firewall installed on your operating system.
+index_bootup=Activate at boot
+index_bootupdesc=Change this option to control whether your firewall is activated at boot time or not.
+index_return=module index
+
+rules_title=Firewall Rules
+rules_none=No firewall rules have been defined yet. All traffic will be allowed through untouched.
+rules_add=Add a new firewall rule.
+rules_sadd=Add a new section separator.
+rules_return=rules list
+rules_log=and log
+rules_move=Move
+rules_service=Services
+rules_no=No.
+rules_enable=Enable Selected
+rules_disable=Disable Selected
+rules_logon=Log Selected
+rules_logoff=Don&#39;t Log Selected
+rules_delete=Delete Selected
+rules_desc=Comment
+
+rule_num=No.
+rule_desc=Rule comment
+rule_source=Source/Output Intf
+rule_dest=Destination/Input Intf
+rule_service=Destination services
+rule_action=Action
+rule_enabled=Enabled
+rule_any=Any
+rule_yes=$yes
+rule_no=<font color=#ff0000>$no</font>
+rule_accept=<font color=#00aa00>Accept</font>
+rule_allow=<font color=#00aa00>Allow</font>
+rule_drop=<font color=#ff0000>Drop</font>
+rule_deny=<font color=#ff0000>Deny</font>
+rule_reject=<font color=#ff4400>Reject</font>
+rule_ignore=Ignore
+rule_title1=Create Rule
+rule_title2=Edit Rule
+rule_title3=Insert Rule
+rule_header=Firewall rule details
+rule_anywhere=Any
+rule_anywheresource=Any host or network
+rule_anywheredest=Any host or network (except the firewall host)
+rule_host=Host or network
+rule_resolv=Resolve hostname to IP address
+rule_named=named
+rule_group=Host groups
+rule_iface=Network interface
+rule_oifc=Other..
+rule_anyserv=Any service
+rule_oneserv=Only services ..
+rule_log=Log as well?
+rule_not=Must not match
+rule_none=None
+rule_err=Failed to save rule
+rule_esource=Missing or invalid source hostname, IP address or network
+rule_edest=Missing or invalid destination hostname, IP address or network
+rule_before=Add Rule Before
+rule_after=Add Rule After
+rule_atpos=Position in list
+rule_end=End of list
+rule_pos=Before rule $1 ($2 -> $3)
+rule_spos=Before separator ($1)
+rule_ename=Invalid group name for host or network
+rule_egroups=No groups selected
+rule_eservices=No services selected
+rule_time=Times
+rule_anytime=Any
+rule_seltime=During selected time
+rule_rusured=Are you sure you want to delete this rule?
+rule_rusures=Are you sure you want to modify this rule?
+rule_goahead=Yes, Do It
+
+sep_title1=Create Section
+sep_title2=Edit Section
+sep_title3=Insert Section
+sep_header=Firewall rule list section details
+sep_desc=Section name
+sep_err=Failed to save section
+sep_edesc=Missing section name
+
+services_title=Services and Protocols
+services_header1=User-defined services
+services_header2=Standard services
+services_add=Add a new service.
+services_return=services list
+
+service_name=Name
+service_ports=Protocols and ports
+service_proto=Protocol
+service_port=Port or port range
+service_title1=Create Service
+service_title2=Edit Service
+service_header=User-defined service details
+service_err=Failed to save service
+service_ename=Missing service name
+service_eport=Missing or invalid TCP or UDP port number, range (like 100-200) or list of ports in row $1
+service_eicmp=Missing or invalid ICMP type number in row $1
+service_eip=Missing or invalid IP protocol number in row $1
+service_enone=No protocols and ports entered
+service_eclash=A service with the same name already exists
+service_eprotos=All protocols must be the same
+service_err2=Failed to delete service
+service_einuse=This service is being used by a rule
+service_einuse2=This service is a member of another service
+service_members=Member services
+
+groups_title=Host and Network Groups
+groups_add=Add a new host and network group.
+groups_none=No host groups have been defined yet.
+groups_return=groups list
+
+group_name=Group name
+group_members=Member hosts / networks
+group_members2=Member groups
+group_title1=Create Group
+group_title2=Edit Group
+group_header=Host and network group details
+group_err=Failed to save group
+group_ename=Missing or invalid group name
+group_eclash=A group with the same name already exists
+group_emember='$1' is not a valid hostname, IP address or network
+group_emembers=No group members entered
+group_neg=Negate
+group_err2=Failed to delete group
+group_einuse=This group is in use by a rule
+group_in_use_nat=This group is in use by a nat rule
+group_in_use_group=This group is in use by the group 
+group_eself=A group cannot contain itself as a member
+group_resolv=Resolve hostnames to IP addresses
+group_eneg=Negation cannot be used with the network range $1
+
+anywhere=Anywhere
+anywhere_but=Anywhere except firewall
+nowhere=Nowhere
+not=Not $1
+iface=Interface $1
+iface_not=Not interface $1
+
+nat_title=Network Address Translation
+nat2_title=Failover NAT
+nat_desc=NAT status
+nat_nets=Networks to translate dynamically
+nat_excl=Except for destinations
+nat_disabled=Disabled
+nat_enabled=Enabled, on external interface
+nat_header=Current NAT settings
+nat_err=Failed to save NAT settings
+nat_eiface=Missing or invalid external interface
+nat_maps=Static external to internal translations
+nat_mapsdesc=If an external interface is selected,<br>a virtual interface will be added<br>to it for the external IP address<br>when the firewall is activated.
+nat_ext=External IP address
+nat_int=Internal IP address
+nat_virt=External interface
+nat_eext=The external name/group has more then 1 IP in row $1
+nat_eint=The internal name/group has more then 1 IP in row $1
+
+
+
+logs_title=View Live Firewall Log
+logs_viewing=Viewing the firewall log file $1 ..
+logs_download=Download
+
+authlogs_title=View Live Security Log
+
+pat_title=Port Address Translation
+pat_desc=PAT status
+pat_disabled=Disabled
+pat_enabled=Enabled, using external interface
+pat_forward=External services to forward
+pat_service=Service
+pat_host=To internal host
+pat_iface=External interface
+pat_header=Current PAT settings
+pat_err=Failed to save PAT settings
+pat_ehost=Missing or invalid internal host in row $1
+pat_eiface=Missing or invalid external interface in row $1
+
+spoof_title=Address Spoofing Prevention
+spoof_desc=Prevention status
+spoof_disabled=Disabled
+spoof_enabled=Enabled, on external interface
+spoof_nets=Internal networks
+spoof_header=Current spoofing prevention settings
+spoof_err=Failed to save address spoofing settings
+spoof_enet='$1' is not a valid network address - must be like 192.168.1.0/24
+spoof_enets=No internal networks were entered
+
+syn_title=Syn Attack Prevention
+syn_header=Current <tt>syn</tt> attack prevention setting
+syn_flood=Block <tt>syn</tt> flood attacks?
+syn_spoof=Block TCP connections that do not start with <tt>syn</tt> packet?
+syn_fin=Block TCP packets with both <tt>syn</tt> and <tt>fin</tt> set?
+
+apply_title=Applying Configuration
+apply_doing=Now applying firewall configuration ..
+apply_done=.. done. Firewall rules are now active, and routing has been enabled.
+apply_failed=.. failed : $1
+apply_return=previous page
+apply_button=Apply Configuration
+apply_ecannot=You are not allowed to apply the firewall configuration
+
+stop_title=Stopping Firewall
+stop_doing=Disabling firewall ..
+stop_done=.. done. All traffic will now be allowed, and but routing has been disabled.
+stop_failed=.. failed : $1
+
+backup_title=Backup Configuration
+backup_header=Firewall settings backup options
+backup_dest=Backup destination
+backup_dest0=Download in browser
+backup_dest1=Save to file on server
+backup_dest3=Email to address
+backup_dest2=FTP to server
+backup_ftpfile=save as file
+backup_ftpuser=Login as user
+backup_ftppass=with password
+backup_what=Objects to backup
+backup_ok=Save and Backup Now
+backup_save=Save Settings
+backup_err=Failed to backup configuration
+backup_edest=Missing or invalid backup destination file
+backup_edestdir=Directory for destination file does not exist
+backup_ewhat=Nothing selected to backup
+backup_ewhat2=None of the selected objects are in use on this firewall
+backup_done1=A backup of the firewall configuration has been successfully saved to $1.
+backup_done2=A backup of the firewall configuration has been successfully uploaded to the FTP server $1 as $2.
+backup_done3=A backup of the firewall configuration has been successfully emailed to $1.
+backup_donesched=The schedule backup settings have been saved.
+backup_pass=Password for backup file
+backup_nopass=None
+backup_epass=No password entered
+backup_ezipcmd=The <tt>zip</tt> and <tt>unzip</tt> commands are needed for the backup and restore functions to work, but are not installed on your system.
+backup_sched=Backup on schedule?
+backup_nosched=No
+backup_interval=Yes, on schedule
+backup_eemail=Missing or invalid-looking email address
+backup_eftphost=Missing or invalid FTP server
+backup_eftpfile=Missing FTP filename
+backup_eftpuser=Missing FTP login name
+
+restore_title=Restore Configuration
+restore_header=Firewall settings restore options
+restore_src=Backup source
+restore_src1=Uploaded file
+restore_src0=File on server
+restore_what=Objects to restore
+restore_ok=Restore Now
+restore_err=Failed to restore configuration
+restore_esrc=The source file on the server does not exist
+restore_efile=No uploaded file was selected
+restore_ewhat=Nothing selected to restore
+restore_etar=The selected backup source file is not in a valid format
+restore_cerr=The backup could not be restored, as the following consistency errors were detected in the restored configuration :
+restore_enat=The group <tt>$1</tt> used for Network Address Translation does not exist.
+restore_epat=The service <tt>$1</tt> used for Port Address Translation does not exist.
+restore_eservice=The service <tt>$1</tt> used in rule $2 does not exist.
+restore_etime=The time range <tt>$1</tt> used in rule $2 does not exist.
+restore_egroup=The group <tt>$1</tt> used in rule $2 does not exist.
+restore_done=The firewall configuration has been successfully restored from the chosen backup file.
+restore_pass=Password for backup file
+restore_epass=Password is incorrect
+restore_epass2=The backup is password protected, but no password was entered
+
+acl_features=Editable module features
+acl_rfeatures=Viewable module features
+acl_all=All
+acl_sel=Selected below ..
+ecannot=You are not allowed to use the <tt>$1</tt> page
+acl_apply=Apply Configuration
+acl_bootup=Activate at boot time
+acl_edit=Can edit firewall settings?
+
+users_desc=The users listed on this page have access to firewall module, with the selected privileges.
+users_title=Firewall Users
+users_add=Add a new firewall user.
+users_name=Username
+users_ips=Allowed IPs
+users_enabled=Enabled?
+users_allow=Only $1
+users_deny=All except $1
+users_all=All
+users_edit=Can edit?
+users_none=No users with access to only the firewall have been created yet.
+users_return=user list
+
+user_title1=Create User
+user_title2=Edit User
+user_header=Firewall user details
+user_name=Login name
+user_pass=Password
+user_same=Don't change
+user_change=Set to
+user_enabled=Login allowed?
+user_err=Failed to save user
+user_ename=Missing username
+user_mods=Available Webmin modules
+
+bootup_ecannot=You are not allowed to enable or disable the firewall at boot time
+
+log_create_rule=Created rule for $1 to $2
+log_update_rule=Modified rule for $1 to $2
+log_delete_rule=Deleted rule for $1 to $2
+log_move_rule=Moved rule for $1 to $2
+log_create_service=Created service named $1
+log_update_service=Modified service named $1
+log_delete_service=Deleted service named $1
+log_create_group=Created host group named $1
+log_update_group=Modified host group named $1
+log_delete_group=Deleted host group named $1
+log_create_user=Created firewall user $1
+log_update_user=Modified firewall user $1
+log_delete_user=Deleted firewall user $1
+log_create_time=Created time range $1
+log_update_time=Modified time range $1
+log_delete_time=Deleted time range $1
+log_create_sep=Created separator $1
+log_update_sep=Modified separator $1
+log_delete_sep=Deleted separator $1
+log_update_nat=Updated NAT setttings
+log_update_pat=Updated PAT setttings
+log_update_spoof=Updated anti-spoofing setttings
+log_syn=Updated syn attack settings
+log_apply=Applied Configuration
+log_bootup=Enabled firewall at boot time
+log_bootdown=Disabled firewall at boot time
+log_backup=Backed up firewall to file $1
+log_backup_file=Backed up firewall to browser
+log_restore=Restored firewall from file $1
+log_restore_file=Restored firewall from uploaded file
+log_oldenable_rules=Enabled $1 rules and disabled $2 rules
+log_enable_rules=Enabled $1 rules
+log_disable_rules=Disabled $1 rules
+log_logon_rules=Turned on logging for $1 rules
+log_logoff_rules=Turned off logging for $1 rules
+log_delete_rules=Deleted $1 rules
+log_import_rules=Imported rules
+log_import_services=Imported services
+log_import_groups=Imported groups
+log_import_times=Imported time ranges
+
+ipsec_title=FreeSWAN Configuration
+
+times_title=Time Ranges
+times_name=Range name
+times_hours=Hours of day
+times_days=Days of week
+times_all=All
+times_add=Add a new time range.
+times_none=No time ranges have been defined yet.
+times_return=time ranges
+
+time_title1=Create Time Range
+time_title2=Edit Time Range
+time_name=Time range name
+time_header=Time range details
+time_hours=Hours of the day
+time_allday=All day
+time_from=From
+time_to=to
+time_days=Days of the week
+time_allweek=All week
+time_sel=Selected days ..
+time_err=Failed to save time range
+time_ename=Missing or invalid range name
+time_eclash=A time range with the same name already exists
+time_efrom=Missing or invalid starting time - must be in HH:MM format
+time_eto=Missing or invalid ending time - must be in HH:MM format
+time_ehourfrom=Invalid hour in start time
+time_ehourto=Invalid hour in end time
+time_eminfrom=Invalid minute in start time
+time_eminto=Invalid minute in end time
+time_edays=No days of the week selected
+time_err2=Failed to delete time range
+time_einuse=This time range is being used by a firewall rule
+
+remote_title=Remote Logging
+remote_header=Remote firewall logging settings
+remote_host=Send logs to remote host?
+remote_port=on port
+remote_to=Yes, to
+remote_user=Login as user
+remote_pass=Login with password
+remote_err=Failed to save remote logging settings
+remote_ehost=Missing or invalid hostname
+remote_euser=Missing username
+remote_econnect=Could not connect to host : $1
+remote_etimeout=Timed out
+
+import_title=Import Objects
+import_header1=Rule import options
+import_desc1=This form can be used to add firewall rules from a file in CSV format. The file must have the following columns:<br><table> <tr><td><b>Source</b></td> <td>The source IP address, hostname or network, or blank for any</td></tr> <tr><td><b>Destination</b></td> <td>The destination IP address, hostname or network, or blank for any</td></tr> <tr><td><b>Services</b></td> <td>The names of services to match, or blank for any</td></tr> <tr><td><b>Action</b></td> <td>The action to take for matching packets. If the action is followed by <i>log</i>, then the rule will be logged too.</b></td></tr> <tr><td><b>Comment</b></td> <td>An optional comment for this rule</td></tr> <tr><td><b>Time</b></td> <td>An optional time range name that the rule applies in</td></tr> </table>
+import_header2=Service import options
+import_desc2=This form can be used to add services from a file in CSV format. The file must have the following columns:<br><table> <tr><td><b>Name</b></td> <td>Name of the service</td> </tr> <tr> <td><b>Protocol</b></td> <td>A protocol like TCP or UDP</td> </tr> <tr> <td><b>Port</b></td> <td>A port number</td> </tr> </table>
+import_header3=Group import options
+import_desc3=This form can be used to add host groups from a file in CSV format. The file must have the following columns:<br><table> <tr><td><b>Name</b></td> <td>Name of the group</td> </tr> <tr> <td><b>Member</b></td> <td>A member IP address, network or host. Multiple member columns can be specified.</td> </tr> </table>
+import_header4=Time range import options
+import_desc4=This form can be used to add time ranges from a file in CSV format. The file must have the following columns:<br><table> <tr><td><b>Name</b></td> <td>Name of the range</td> </tr> <tr><td><b>Days</b></td> <td>Days of the week this range applies, or empty for all.</td></tr> <tr><td><b>Times</b></td> <td>Starting and ending times separated by a -, or empty for all.</td></tr> </table>
+import_src=CSV file source
+import_ok=Import Now
+import_err=Import failed
+import_erow=Line $1 of the CSV file is invalid : <tt>$2</tt>
+import_esource=Invalid source address or network on line $1 : <tt>$2</tt>
+import_edest=Invalid destination address or network on line $1 : <tt>$2</tt>
+import_eservice=Unknown service on line $1 : <tt>$2</tt>
+import_etime=Unknown time range on line $1 : <tt>$2</tt>
+import_eaction=Unknown action on line $1 : <tt>$2</tt>
+import_eservname=Missing service name on line $1
+import_eservclash=Service name is already in use on line $1 : <tt>$2</tt>
+import_eproto=Missing or unknown protocol on line $1 : <tt>$2</tt>
+import_eport=Missing or invalid port number on line $1 : <tt>$2</tt>
+import_egroupname=Missing group name on line $1
+import_egroupclash=Group name is already in use on line $1 : <tt>$2</tt>
+import_ehost=Invalid host name on line $1 : <tt>$2</tt>
+import_etimename=Missing time name on line $1
+import_etimeclash=Time name is already in use on line $1 : <tt>$2</tt>
+import_etimeday=Invalid day of the week on line $1 : <tt>$2</tt>
+import_etimehour=Invalid time range line $1 : <tt>$2</tt>
+import_etimenone=No days or times specified on line $1
+import_done1=$1 firewall rules have been successfully imported.
+import_done2=$1 services have been successfully imported.
+import_done3=$1 groups have been successfully imported.
+import_done4=$1 time ranges have been successfully imported.
+
+report_title=Search Log
+report_pos=Log entries $1 to $2 of $3
+report_action=Action
+report_date=Date
+report_time=Time
+report_src=Source
+report_dst=Destination
+report_dst_iface=Interface
+report_proto=Protocol
+report_src_port=Source port
+report_dst_port=Dest port
+report_first=Log time
+report_last=Log time
+report_rule=Rule no.
+report_rule2=Rule
+report_none=No matching firewall log entries were found.
+report_none2=No firewall log entries have been recorded yet.
+report_search=Find log entries where ..
+report_reset=Show all entries
+report_mode0=Ignored
+report_mode1=Equals
+report_mode2=Not equals
+report_mode1first=After
+report_mode1last=Before
+report_welf=Export in WELF Format
+report_welfdesc=Click this button to export all the matching firewall log entries in WELF format, for analysis using programs that support this format.
+report_err=Failed to export logs
+report_done=Successfully exported $1 log entries in WELF format to $2.
+report_save=Save Search
+report_savedesc=Enter a name below and click this button to save this search for later user.
+report_savename=Search name
+report_load=Load Search
+report_loaddesc=Select a saved search below and click this button to display matching log entries.
+report_esave=Missing or invalid name for saved search
+report_usesaved=Use saved search
+report_return=firewall log
+
+other=Other..
+
+searches_title=Saved Searches
+config_title=Module Configuration
+
+welf_title=WELF Export
+welf_header=WELF log entries export
+welf_dest=Send WELF file to
+welf_err=Failed to export logs
+welf_done1=Firewall logs in WELF format have been successfully saved to $1.
+welf_done2=Firewall logs in WELF format have been successfully uploaded to the FTP server $1 as $2.
+welf_done3=Firewall logs in WELF format have been successfully emailed to $1.
+welf_ok=Export Now
+
+bandwidth_title=Bandwidth Monitoring
+bandwidth_header=Setup bandwidth usage monitoring
+bandwidth_enabled=Monitor network traffic for bandwidth reporting?
+bandwidth_yes=Yes, on interface
+
+monitor_type=Simple Firewall Rule
+monitor_rule=Fail when rule is hit
+monitor_num=Rule $1 ($2 -> $3)
+
diff --git a/itsecur-firewall/list_authlogs.cgi b/itsecur-firewall/list_authlogs.cgi
new file mode 100755 (executable)
index 0000000..5648190
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+# list_logs.cgi
+# Real-time view of the security log file
+
+require './itsecur-lib.pl';
+&can_use_error("authlogs");
+$theme_no_table++;
+$| = 1;
+&header($text{'authlogs_title'}, "");
+print "<hr>\n";
+
+$log = $config{'authlog'} || &get_authlog_file();
+print "<b>",&text('logs_viewing', "<tt>$log</tt>"),"</b><p>\n";
+print "<applet code=LogViewer width=90% height=70%>\n";
+print "<param name=url value='authtail.cgi'>\n";
+print "<param name=pause value=1>\n";
+print "<param name=buttonlink value=authdownload.cgi>\n";
+print "<param name=buttonname value='$text{'logs_download'}'>\n";
+if ($session_id) {
+       print "<param name=session value=\"sid=$session_id\">\n";
+       }
+print "</applet>\n";
+print "</form>\n";
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+
diff --git a/itsecur-firewall/list_backup.cgi b/itsecur-firewall/list_backup.cgi
new file mode 100755 (executable)
index 0000000..428f11f
--- /dev/null
@@ -0,0 +1,88 @@
+#!/usr/bin/perl
+# Show a form for backing up some or all firewall objects
+
+require './itsecur-lib.pl';
+&can_edit_error("backup");
+&check_zip();
+&header($text{'backup_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+print "<form action=backup.cgi/firewall.zip method=post>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'backup_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+# Show destination
+($mode, @dest) = &parse_backup_dest($config{'backup_dest'});
+print "<tr> <td valign=top><b>$text{'backup_dest'}</b></td>\n";
+print "<td><table cellpadding=0 cellspacing=0>\n";
+printf "<tr> <td><input type=radio name=dest_mode value=0 %s></td> <td>%s</td> </tr>\n",
+       $mode == 0 ? "checked" : "", $text{'backup_dest0'};
+
+printf "<tr> <td><input type=radio name=dest_mode value=1 %s></td> <td>%s</td>\n",
+       $mode == 1 ? "checked" : "", $text{'backup_dest1'};
+printf "<td colspan=3><input name=dest size=40 value='%s'> %s</td> </tr>\n",
+       $mode == 1 ? $dest[0] : "", &file_chooser_button("dest");
+
+printf "<tr> <td><input type=radio name=dest_mode value=3 %s></td> <td>%s</td>\n",
+       $mode == 3 ? "checked" : "", $text{'backup_dest3'};
+printf "<td colspan=3><input name=email size=40 value='%s'></td> </tr>\n",
+       $mode == 3 ? $dest[0] : "";
+
+printf "<tr> <td><input type=radio name=dest_mode value=2 %s></td>\n",
+       $mode == 2 ? "checked" : "";
+printf "<td>%s</td> <td><input name=ftphost size=20 value='%s'></td>\n",
+       $text{'backup_dest2'}, $mode == 2 ? $dest[2] : "";
+printf "<td>%s</td> <td><input name=ftpfile size=20 value='%s'></td> </tr>\n",
+       $text{'backup_ftpfile'}, $mode == 2 ? $dest[3] : "";
+printf "<tr> <td></td> <td>%s</td> <td><input name=ftpuser size=15 value='%s'></td>\n",
+       $text{'backup_ftpuser'}, $mode == 2 ? $dest[0] : "";
+printf "<td>%s</td> <td><input name=ftppass type=password size=15 value='%s'></td> </tr>\n",
+       $text{'backup_ftppass'}, $mode == 2 ? $dest[1] : "";
+print "</table></td> </tr>\n";
+
+# Show password
+print "<tr> <td valign=top><b>$text{'backup_pass'}</b></td> <td>\n";
+printf "<input type=radio name=pass_def value=1 %s> %s\n",
+       $config{'backup_pass'} ? "" : "checked", $text{'backup_nopass'};
+printf "<input type=radio name=pass_def value=0 %s>\n",
+       $config{'backup_pass'} ? "checked" : "";
+printf "<input type=password name=pass value='%s'></td> </tr>\n",
+       $config{'backup_pass'};
+
+# Show what to backup
+%what = map { $_, 1 } split(/\s+/, $config{'backup_what'});
+print "<tr> <td valign=top><b>$text{'backup_what'}</b></td> <td>\n";
+foreach $w (@backup_opts) {
+       printf "<input type=checkbox name=what value=%s %s> %s<br>\n",
+               $w, $what{$w} ? "checked" : "", $text{$w."_title"};
+       }
+if (defined(&select_all_link)) {
+       print &select_all_link("what", 0),"\n";
+       print &select_invert_link("what", 0),"\n";
+       }
+print "</td> </tr>\n";
+
+# Show schedule
+$job = &find_backup_job();
+print "<tr> <td valign=top><b>$text{'backup_sched'}</b></td> <td>\n";
+printf "<input type=radio name=sched_def value=1 %s> %s\n",
+       $job ? "" : "checked", $text{'backup_nosched'};
+printf "<input type=radio name=sched_def value=0 %s> %s\n",
+       $job ? "checked" : "", $text{'backup_interval'};
+print "<select name=sched>\n";
+foreach $s ("hourly", "daily", "weekly", "monthly", "yearly") {
+       printf "<option value=%s %s>%s\n",
+               $s, $job && $job->{'special'} eq $s ? "selected" : "",
+               ucfirst($s);
+       }
+print "</select></td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+print "<input type=submit value='$text{'backup_ok'}'>\n";
+print "<input type=submit name=save value='$text{'backup_save'}'></form>\n";
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+
diff --git a/itsecur-firewall/list_bandwidth.cgi b/itsecur-firewall/list_bandwidth.cgi
new file mode 100755 (executable)
index 0000000..3ea3f76
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+# Show a form for setting up bandwidth monitoring
+
+require './itsecur-lib.pl';
+&can_edit_error("bandwidth");
+&header($text{'bandwidth_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+print "<form action=save_bandwidth.cgi method=post>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'bandwidth_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+print "<tr> <td><b>$text{'bandwidth_enabled'}</b></td> <td>\n";
+printf "<input type=radio name=enabled value=0 %s> %s\n",
+       $config{'bandwidth'} ? "" : "checked", $text{'no'};
+printf "<input type=radio name=enabled value=1 %s> %s\n",
+       $config{'bandwidth'} ? "checked" : "", $text{'bandwidth_yes'};
+print &iface_input("iface", $config{'bandwidth'}, 1, 1, 0);
+print "</td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+print "<input type=submit value='$text{'save'}'></form>\n";
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+
diff --git a/itsecur-firewall/list_groups.cgi b/itsecur-firewall/list_groups.cgi
new file mode 100755 (executable)
index 0000000..bf60d47
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+# list_groups.cgi
+# Displays a list of host and network groups
+
+require './itsecur-lib.pl';
+&can_use_error("groups");
+&header($text{'groups_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+@groups = &list_groups();
+$edit = &can_edit("groups");
+if (@groups) {
+       print "<a href='edit_group.cgi?new=1'>$text{'groups_add'}</a><br>\n"
+               if ($edit);
+       print "<table border>\n";
+       print "<tr $tb> <td><b>$text{'group_name'}</b></td> ",
+             "<td><b>$text{'group_members'}</b></td> </tr>\n";
+       foreach $g (@groups) {
+               print "<tr $cb>\n";
+               print "<td><a href='edit_group.cgi?idx=$g->{'index'}'>",
+                     "$g->{'name'}</a></td>\n";
+               @mems = @{$g->{'members'}};
+               if (@mems > 5) {
+                       @mems = (@mems[0..4], "...");
+                       }
+               print "<td>",join(" , ",
+                                 map { &group_name($_) } @mems),"</td>\n";
+               print "</tr>\n";
+               }
+       print "</table>\n";
+       }
+else {
+       print "<b>$text{'groups_none'}</b><p>\n";
+       }
+print "<a href='edit_group.cgi?new=1'>$text{'groups_add'}</a><p>\n"
+       if ($edit);
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
diff --git a/itsecur-firewall/list_import.cgi b/itsecur-firewall/list_import.cgi
new file mode 100755 (executable)
index 0000000..cae9e7e
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+# Show a form for importing rules in CSV format
+
+require './itsecur-lib.pl';
+&can_edit_error("import");
+&check_zip();
+&header($text{'import_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+
+foreach $i (1 .. (&supports_time() ? 4 : 3)) {
+       $prog = $i == 1 ? "import_rules.cgi" :
+               $i == 2 ? "import_servs.cgi" :
+               $i == 3 ? "import_groups.cgi" :
+               $i == 4 ? "import_times.cgi" : undef;
+       print "<hr>\n";
+       print $text{'import_desc'.$i},"<p>\n";
+       print "<form action=$prog enctype=multipart/form-data method=post>\n";
+       print "<table border>\n";
+       print "<tr $tb> <td><b>",$text{'import_header'.$i},"</b></td> </tr>\n";
+       print "<tr $cb> <td><table>\n";
+
+       # Show source
+       print "<tr> <td valign=top><b>$text{'import_src'}</b></td> <td>\n";
+       printf "<input type=radio name=src_def value=1 %s> %s\n",
+               $mode != 1 ? "checked" : "", $text{'restore_src1'};
+       print "<input name=file type=file size=20><br>\n";
+       printf "<input type=radio name=src_def value=0 %s> %s\n",
+               $mode == 1 ? "checked" : "", $text{'restore_src0'};
+       printf "<input name=src size=40 value='%s'> %s</td> </tr>\n",
+               $mode == 1 ? $dest[0] : undef, &file_chooser_button("src");
+
+       print "</table></td></tr></table>\n";
+       print "<input type=submit value='$text{'import_ok'}'></form>\n";
+       }
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+
diff --git a/itsecur-firewall/list_logs.cgi b/itsecur-firewall/list_logs.cgi
new file mode 100755 (executable)
index 0000000..2b56fb8
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+# list_logs.cgi
+# Real-time view of a log file
+
+require './itsecur-lib.pl';
+&can_use_error("logs");
+$theme_no_table++;
+$| = 1;
+&header($text{'logs_title'}, "");
+print "<hr>\n";
+
+$log = $config{'log'} || &get_log_file();
+print "<b>",&text('logs_viewing', "<tt>$log</tt>"),"</b><p>\n";
+print "<applet code=LogViewer width=90% height=70%>\n";
+print "<param name=url value='tail.cgi'>\n";
+print "<param name=pause value=1>\n";
+print "<param name=buttonlink value=download.cgi>\n";
+print "<param name=buttonname value='$text{'logs_download'}'>\n";
+if ($session_id) {
+       print "<param name=session value=\"sid=$session_id\">\n";
+       }
+print "</applet>\n";
+print "</form>\n";
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+
diff --git a/itsecur-firewall/list_nat.cgi b/itsecur-firewall/list_nat.cgi
new file mode 100755 (executable)
index 0000000..c344d90
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+# list_nat.cgi
+# Show NAT enable form
+
+require './itsecur-lib.pl';
+&can_use_error("nat");
+&header($text{'nat_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+print "<form action=save_nat.cgi>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'nat_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+($iface, @nets) = &get_nat();
+@maps = grep { ref($_) } @nets;
+@nets = grep { !ref($_) } @nets;
+print "<tr> <td valign=top><b>$text{'nat_desc'}</b></td> <td colspan=3>\n";
+printf "<input type=radio name=nat value=0 %s> %s<br>\n",
+       $iface ? "" : "checked", $text{'nat_disabled'};
+printf "<input type=radio name=nat value=1 %s> %s\n",
+       $iface ? "checked" : "", $text{'nat_enabled'};
+print &iface_input("iface", $iface);
+print "</td> </tr>\n";
+
+print "<tr> <td valign=top><b>$text{'nat_nets'}</b></td>\n";
+print "<td valign=top><table>\n";
+$i = 0;
+foreach $n ((grep { $_ !~ /^\!/ } @nets), undef, undef, undef) {
+       print "<tr> <td>",&group_input("net_$i", $n, 1),"</td> </tr>\n";
+       $i++;
+       }
+print "</table></td>\n";
+
+print "<td valign=top><b>$text{'nat_excl'}</b></td>\n";
+print "<td valign=top><table>\n";
+$i = 0;
+foreach $n ((grep { $_ =~ /^\!/ } @nets), undef, undef, undef) {
+       print "<tr> <td>",&group_input("excl_$i", $n =~ /^\!(.*)/ ? $1 : undef, 1),"</td> </tr>\n";
+       $i++;
+       }
+print "</table></td> </tr>\n";
+
+print "<tr> <td valign=top><b>$text{'nat_maps'}</b>",
+      "<br>$text{'nat_mapsdesc'}</td> <td colspan=3>\n";
+print "<table>\n";
+print "<tr> <td><b>$text{'nat_ext'}</b></td> ",
+      "<td><b>$text{'nat_int'}</b></td> ",
+      "<td><b>$text{'nat_virt'}</b></td> </tr>\n";
+$i = 0;
+foreach $m (@maps, [ ], [ ], [ ]) {
+       #print "<tr>\n";
+       #printf "<td><input name=ext_%d size=20 value='%s'></td>\n",
+       #       $i, $m->[0];
+       #printf "<td><input name=int_%d size=20 value='%s'></td>\n",
+       #       $i, $m->[1];
+       #print "<td>",&iface_input("virt_$i", $m->[2], 1, 1, 1),"</td>\n";
+       #print "</tr>\n";
+       print "<tr>"    ;       
+       printf "<td><input name=ext_%d size=20 value='%s'></td>\n",
+               $i, $m->[0];
+       print "<td>",&group_input("int_$i", $m->[1], 1),"</td>\n";
+       print "<td>",&iface_input("virt_$i", $m->[2], 1, 1, 1),"</td>\n";               
+       print "</tr>\n";
+       $i++;
+       }
+print "</table></td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+print "<input type=submit value='$text{'save'}'></form>\n";
+&can_edit_disable("nat");
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
diff --git a/itsecur-firewall/list_nat2.cgi b/itsecur-firewall/list_nat2.cgi
new file mode 100755 (executable)
index 0000000..0612a8d
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/perl
+# list_nat.cgi
+# Show NAT enable form
+
+require './itsecur-lib.pl';
+&can_use_error("nat2");
+&header($text{'nat2_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+print "<form action=save_nat2.cgi>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'nat_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+($iface, @nets) = &get_nat($nat2_file);
+@maps = grep { ref($_) } @nets;
+@nets = grep { !ref($_) } @nets;
+print "<tr> <td valign=top><b>$text{'nat_desc'}</b></td> <td colspan=3>\n";
+printf "<input type=radio name=nat value=0 %s> %s<br>\n",
+       $iface ? "" : "checked", $text{'nat_disabled'};
+printf "<input type=radio name=nat value=1 %s> %s\n",
+       $iface ? "checked" : "", $text{'nat_enabled'};
+print &iface_input("iface", $iface);
+print "</td> </tr>\n";
+
+print "<tr> <td valign=top><b>$text{'nat_nets'}</b></td>\n";
+print "<td valign=top><table>\n";
+$i = 0;
+foreach $n ((grep { $_ !~ /^\!/ } @nets), undef, undef, undef) {
+       print "<tr> <td>",&group_input("net_$i", $n, 1),"</td> </tr>\n";
+       $i++;
+       }
+print "</table></td>\n";
+
+print "<td valign=top><b>$text{'nat_excl'}</b></td>\n";
+print "<td valign=top><table>\n";
+$i = 0;
+foreach $n ((grep { $_ =~ /^\!/ } @nets), undef, undef, undef) {
+       print "<tr> <td>",&group_input("excl_$i", $n =~ /^\!(.*)/ ? $1 : undef, 1),"</td> </tr>\n";
+       $i++;
+       }
+print "</table></td> </tr>\n";
+
+print "<tr> <td valign=top><b>$text{'nat_maps'}</b>",
+      "<br>$text{'nat_mapsdesc'}</td> <td colspan=3>\n";
+print "<table>\n";
+print "<tr> <td><b>$text{'nat_ext'}</b></td> ",
+      "<td><b>$text{'nat_int'}</b></td> ",
+      "<td><b>$text{'nat_virt'}</b></td> </tr>\n";
+$i = 0;
+foreach $m (@maps, [ ], [ ], [ ]) {
+       print "<tr>\n";
+       printf "<td><input name=ext_%d size=20 value='%s'></td>\n",
+               $i, $m->[0];
+       printf "<td><input name=int_%d size=20 value='%s'></td>\n",
+               $i, $m->[1];
+       print "<td>",&iface_input("virt_$i", $m->[2], 1, 1, 1),"</td>\n";
+       print "</tr>\n";
+       $i++;
+       }
+print "</table></td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+print "<input type=submit value='$text{'save'}'></form>\n";
+&can_edit_disable("nat");
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
diff --git a/itsecur-firewall/list_pat.cgi b/itsecur-firewall/list_pat.cgi
new file mode 100755 (executable)
index 0000000..beaa72d
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+# list_pat.cgi
+# Show table of incoming forwarded ports
+
+require './itsecur-lib.pl';
+&can_use_error("pat");
+&header($text{'pat_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+@forwards = &get_pat();
+print "<form action=save_pat.cgi>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'pat_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+print "<tr> <td valign=top><b>$text{'pat_forward'}</b></td> ",
+      "<td><table border>\n";
+print "<tr $tb> <td><b>$text{'pat_service'}</b></td> ",
+      "<td><b>$text{'pat_host'}</b></td> ",
+      "<td><b>$text{'pat_iface'}</b></td> </tr>\n";
+$j = 0;
+foreach $f (@forwards, { }, { }, { }) {
+       print "<tr $cb>\n";
+       print "<td>",&service_input("service_$j", $f->{'service'}, 1),"</td>\n";
+       print "<td><input name=host_$j size=30 value='$f->{'host'}'></td>\n";
+       print "<td>",&iface_input("iface_$j", $f->{'iface'},
+                                 0, 1, 1),"</td>\n";
+       print "</tr>\n";
+       $j++;
+       }
+print "</table></td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+print "<input type=submit value='$text{'save'}'></form>\n";
+&can_edit_disable("pat");
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
diff --git a/itsecur-firewall/list_remote.cgi b/itsecur-firewall/list_remote.cgi
new file mode 100755 (executable)
index 0000000..5c32390
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+# Show a form for setting up remote logging
+
+require './itsecur-lib.pl';
+&foreign_require("servers", "servers-lib.pl");
+&can_edit_error("remote");
+&header($text{'remote_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+print "<form action=save_remote.cgi method=post>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'remote_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+@servers = &servers::list_servers();
+($server) = grep { $_->{'host'} eq $config{'remote_log'} } @servers;
+
+# Show target host
+print "<tr> <td><b>$text{'remote_host'}</b></td> <td>\n";
+printf "<input type=radio name=host_def value=1 %s> %s\n",
+       $server ? "" : "checked", $text{'no'};
+printf "<input type=radio name=host_def value=0 %s> %s\n",
+       $server ? "checked" : "", $text{'remote_to'};
+printf "<input name=host size=20 value='%s'> %s\n",
+       $server ? $server->{'host'} : "", $text{'remote_port'};
+printf "<input name=port size=10 value='%s'></td> </tr>\n",
+       $server ? $server->{'port'} : 10000;
+
+# Show login and password
+print "<tr> <td><b>$text{'remote_user'}</b></td> <td>\n";
+printf "<input name=user size=20 value='%s'></td> </tr>\n",
+       $server ? $server->{'user'} : "";
+
+print "<tr> <td><b>$text{'remote_pass'}</b></td> <td>\n";
+printf "<input name=pass type=password size=20 value='%s'></td> </tr>\n",
+       $server ? $server->{'pass'} : "";
+
+print "</table></td></tr></table>\n";
+print "<input type=submit value='$text{'save'}'></form>\n";
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+
diff --git a/itsecur-firewall/list_report.cgi b/itsecur-firewall/list_report.cgi
new file mode 100755 (executable)
index 0000000..c2037f4
--- /dev/null
@@ -0,0 +1,293 @@
+#!/usr/bin/perl
+# Show last few log entries, nicely parsed, with search form
+
+require './itsecur-lib.pl';
+&can_use_error("report");
+use POSIX;
+&ReadParse();
+print "Refresh: $config{'refresh'}\r\n"
+       if ($config{'refresh'});
+&header($text{'report_title'}, "");
+print "<hr>\n";
+
+if ($in{'reset'}) {
+       # Clear all inputs
+       %in = ( );
+       }
+elsif ($in{'save_name'}) {
+       # Load up an old search
+       $search = &get_search($in{'save_name'});
+       if ($search) {
+               $oldstart = $in{'start'};
+               $oldend = $in{'end'};
+               %in = %$search;
+               $in{'start'} = $oldstart;
+               $in{'end'} = $oldend;
+               }
+       }
+
+# Show search form
+print "<form action=list_report.cgi>\n";
+print "<table width=100%>\n";
+print "<tr>\n";
+print "<td colspan=4><input type=submit value='$text{'report_search'}'></td>\n";
+print "<td colspan=4 align=right><input type=submit name=reset value='$text{'report_reset'}'></td>\n";
+print "</tr>\n";
+$i = 0;
+foreach $f (@search_fields) {
+       print "<tr>\n" if ($i%2 == 0);
+       print "<td><b>",$text{'report_'.$f},"</b></td>\n";
+       print "<td><select name=${f}_mode>\n";
+       if ($f eq "first" || $f eq "last") {
+               foreach $m (0 .. 1) {
+                       printf "<option value=%d %s>%s\n",
+                               $m, $in{"${f}_mode"} == $m ? "selected" : "",
+                               $text{'report_mode'.$m.$f} ||
+                               $text{'report_mode'.$m};
+                       }
+               }
+       else {
+               foreach $m (0 .. 2) {
+                       printf "<option value=%d %s>%s\n",
+                               $m, $in{"${f}_mode"} == $m ? "selected" : "",
+                               $text{'report_mode'.$m};
+                       }
+               }
+       print "</select></td>\n";
+       if ($f eq "dst_iface") {
+               print "<td>",&iface_input($f."_what", $in{$f."_what"}),"</td>\n";
+               }
+       elsif ($f eq "proto") {
+               print "<td>",&protocol_input($f."_what", $in{$f."_what"}),"</td>\n";
+               }
+       elsif ($f eq "dst_port" || $f eq "src_port") {
+               print "<td>",&service_input($f."_what", $in{$f."_what"}, 2, 0, 1);
+               printf "<input name=%s_other size=6 value='%s'>\n",
+                       $f, $in{$f."_other"};
+               print "</td>\n";
+               }
+       elsif ($f eq "src" || $f eq "dst") {
+               print "<td>",&group_input($f."_what", $in{$f."_what"}, 2, 0);
+               printf "<input name=%s_other size=10 value='%s'>\n",
+                       $f, $in{$f."_other"};
+               print "</td>\n";
+               }
+       elsif ($f eq "first" || $f eq "last") {
+               print "<td>";
+               &date_input($in{$f."_day"}, $in{$f."_month"},
+                                 $in{$f."_year"}, $f);
+               if ($f eq "first") {
+                       &hourmin_input($in{$f."_hour"} || "00",
+                                      $in{$f."_min"} || "00", $f);
+                       }
+               else {
+                       &hourmin_input($in{$f."_hour"} || "23",
+                                      $in{$f."_min"} || "59", $f);
+                       }
+               print "</td>";
+               }
+       elsif ($f eq "action") {
+               print "<td>",&action_input($f."_what",
+                                          $in{$f."_what"}, 1),"</td>\n";
+               }
+       elsif ($f eq "rule") {
+               printf "<td><input name=%s_what size=5 value='%s'></td>\n",
+                       $f, $in{$f."_what"};
+               }
+       else {
+               printf "<td><input name=%s_what size=20 value='%s'></td>\n",
+                       $f, $in{$f."_what"};
+               }
+       print "<td>&nbsp;&nbsp;</td>\n";
+       print "</tr>\n" if ($i++%2 == 1);
+       }
+
+# Show saved search
+@searches = &list_searches();
+if (@searches) {
+       print "<tr> <td></td> </tr>\n";
+       print "<tr> <td><b>$text{'report_usesaved'}</b></td>\n";
+       print "<td colspan=3><select name=save_name>\n";
+       printf "<option value='' %s>%s\n",
+               $in{'save_name'} eq "" ? "selected" : "", "&nbsp;";
+       foreach $s ( @searches) {
+               printf "<option value='%s' %s>%s\n",
+                       $s->{'save_name'},
+                       $in{'save_name'} eq $s->{'save_name'} ? "selected" : "",
+                       $s->{'save_name'};
+               }
+       print "</select></td> </tr>\n";
+       }
+
+print "</table>\n";
+
+print "</form>\n";
+print "<hr>\n";
+
+# Find those matching current search
+@logs = &parse_all_logs();
+$anylogs = @logs;
+@logs = &filter_logs(\@logs, \%in, \@searchvars);
+if ($in{'save_name'}) {
+       push(@searchvars, "save_name=".&urlize($in{'save_name'}));
+       }
+
+# Show matching log entries
+if (@logs) {
+       if (@searchvars) {
+               $prog = "list_report.cgi?".join("&", @searchvars)."&";
+               }
+       else {
+               $prog = "list_report.cgi?";
+               }
+       if (@logs > $config{'perpage'}) {
+               # Need to show arrows
+               print "<center>\n";
+               $s = int($in{'start'});
+               $e = $in{'start'} + $config{'perpage'} - 1;
+               $e = @logs-1 if ($e >= @logs);
+               if ($s) {
+                       printf "<a href='%sstart=%d'>%s</a>\n",
+                           $prog, 0,
+                           "<img src=images/lleft.gif border=0 align=middle alt='First page'>";
+                       printf "<a href='%sstart=%d'>%s</a>\n",
+                           $prog, $s - $config{'perpage'},
+                           "<img src=/images/left.gif border=0 align=middle alt='Previous page'>";
+                       }
+               print "<font size=+1>",&text('report_pos', $s+1, $e+1,
+                                            scalar(@logs)),"</font>\n";
+               if ($e < @logs-1) {
+                       printf "<a href='%sstart=%d'>%s</a>\n",
+                           $prog, $s + $config{'perpage'},
+                           "<img src=/images/right.gif border=0 align=middle alt='Next page'>";
+                       printf "<a href='%sstart=%d'>%s</a>\n",
+                           $prog, int((@logs-1)/$config{'perpage'})*$config{'perpage'},
+                           "<img src=images/rright.gif border=0 align=middle alt='Last page'>";
+                       }
+               print "</center>\n";
+               }
+       else {
+               # Can show them all
+               $s = 0;
+               $e = @logs - 1;
+               }
+
+       print "<table border width=100%>\n";
+       print "<tr $tb> <td><b>$text{'report_action'}</b></td> ",
+             "<td><b>$text{'report_rule2'}</b></td> ",
+             "<td><b>$text{'report_date'}</b></td> ",
+             "<td><b>$text{'report_time'}</b></td> ",
+             "<td><b>$text{'report_src'}</b></td> ",
+             "<td><b>$text{'report_dst'}</b></td> ",
+             "<td><b>$text{'report_dst_iface'}</b></td> ",
+             "<td><b>$text{'report_proto'}</b></td> ",
+             "<td><b>$text{'report_src_port'}</b></td> ",
+             "<td><b>$text{'report_dst_port'}</b></td> ",
+             "</tr>\n";
+       for($i=$s; $i<=$e; $i++) {
+               $l = $logs[$i];
+               print "<tr $cb>\n";
+               print "<td>",$text{'rule_'.$l->{'action'}},"</td>\n";
+               print "<td>",$l->{'rule'} || "<br>","</td>\n";
+               local @tm = localtime($l->{'time'});
+               print "<td>",strftime("%d/%m/%Y", @tm),"</td>\n";
+               print "<td>",strftime("%H:%M:%S", @tm),"</td>\n";
+               print "<td>",$l->{'src'},"</td>\n";
+               print "<td>",$l->{'dst'},"</td>\n";
+               print "<td>",$l->{'dst_iface'} || "<br>","</td>\n";
+               print "<td>",$l->{'proto'} || "<br>","</td>\n";
+               print "<td>",$l->{'src_port'} || "<br>","</td>\n";
+               print "<td>",$l->{'dst_port'} || "<br>","</td>\n";
+               print "</tr>\n";
+               }
+       print "</table>\n";
+       }
+elsif ($anylogs) {
+       print "<b>$text{'report_none'}</b><p>\n";
+       }
+else {
+       print "<b>$text{'report_none2'}</b><p>\n";
+       }
+
+
+print "<hr>\n";
+print "<table width=100%>\n";
+
+if (@logs && &can_edit("report")) {
+       # Show export button
+       print "<form action=list_welf.cgi>\n";
+       foreach $i (keys %in) {
+               print "<input type=hidden name=$i value='",
+                     &html_escape($in{$i}),"'>\n";
+               }
+       print "<tr> <td valign=top><input type=submit value='$text{'report_welf'}'></td>\n";
+       print "<td>$text{'report_welfdesc'}</td>\n";
+       print "</tr></form>\n";
+       $anyrows++;
+       }
+
+if (@searchvars && &can_edit("report")) {
+       # Show button to save this search
+       print "<form action=save_search.cgi>\n";
+       foreach $i (keys %in) {
+               print "<input type=hidden name=$i value='",
+                     &html_escape($in{$i}),"'>\n";
+               }
+       print "<tr> <td valign=top><input type=submit value='$text{'report_save'}'></td>\n";
+       print "<td>$text{'report_savedesc'}<br>\n";
+       print "<b>$text{'report_savename'}</b>\n";
+       printf "<input name=save_name size=30 value='%s'>\n",
+               $in{'save_name'};
+       print "</td>\n";
+       print "</tr></form>\n";
+       $anyrows++;
+       }
+
+# Show button to select an old search
+#@searches = &list_searches();
+#if (@searches) {
+#      print "<form action=list_report.cgi>\n";
+#      print "<tr> <td valign=top><input type=submit value='$text{'report_load'}'></td>\n";
+#      print "<td>$text{'report_loaddesc'}<br>\n";
+#      print "<b>$text{'report_savename'}</b>\n";
+#      print "<select name=save_name>\n";
+#      foreach $s (@searches) {
+#              printf "<option %s>%s\n",
+#                      $s->{'save_name'} eq $in{'save_name'} ? "selected" : "",
+#                      &html_escape($s->{'save_name'});
+#              }
+#      print "</select>\n";
+#      print "</td>\n";
+#      print "</tr></form>\n";
+#      $anyrows++;
+#      }
+
+print "</table>\n";
+
+print "<hr>\n" if ($anyrows);
+&footer("", $text{'index_return'});
+
+# date_input(day, month, year, prefix)
+sub date_input
+{
+print "<input name=$_[3]_day size=2 value='$_[0]'>";
+print "/<select name=$_[3]_month>\n";
+local $m;
+foreach $m (1..12) {
+       printf "<option value=%d %s>%s\n",
+               $m, $_[1] eq $m ? 'selected' : '', $text{"smonth_$m"};
+       }
+print "</select>";
+print "/<input name=$_[3]_year size=4 value='$_[2]'>";
+print &date_chooser_button("$_[3]_day", "$_[3]_month", "$_[3]_year");
+print "\n";
+}
+
+# hourmin_input(hour, min, prefix)
+sub hourmin_input
+{
+print "<input name=$_[2]_hour size=2 value='$_[0]'>";
+print ":";
+print "<input name=$_[2]_min size=2 value='$_[1]'>";
+print "\n";
+}
diff --git a/itsecur-firewall/list_restore.cgi b/itsecur-firewall/list_restore.cgi
new file mode 100755 (executable)
index 0000000..4fe830e
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/perl
+# Show a form for restoring some or all firewall objects
+
+require './itsecur-lib.pl';
+&can_edit_error("restore");
+&check_zip();
+&header($text{'restore_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+($mode, @dest) = &parse_backup_dest($config{'backup_dest'});
+print "<form action=restore.cgi enctype=multipart/form-data method=post>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'restore_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+# Show source
+print "<tr> <td valign=top><b>$text{'restore_src'}</b></td> <td>\n";
+printf "<input type=radio name=src_def value=1 %s> %s\n",
+       $mode != 1 ? "checked" : "", $text{'restore_src1'};
+print "<input name=file type=file size=20><br>\n";
+printf "<input type=radio name=src_def value=0 %s> %s\n",
+       $mode == 1 ? "checked" : "", $text{'restore_src0'};
+printf "<input name=src size=40 value='%s'> %s</td> </tr>\n",
+       $mode == 1 ? $dest[0] : undef, &file_chooser_button("src");
+
+# Show password
+print "<tr> <td valign=top><b>$text{'restore_pass'}</b></td> <td>\n";
+printf "<input type=radio name=pass_def value=1 %s> %s\n",
+       $config{'backup_pass'} ? "" : "checked", $text{'backup_nopass'};
+printf "<input type=radio name=pass_def value=0 %s>\n",
+       $config{'backup_pass'} ? "checked" : "";
+printf "<input type=password name=pass value='%s'></td> </tr>\n",
+       $config{'backup_pass'};
+
+# Show what to restore
+%what = map { $_, 1 } split(/\s+/, $config{'backup_what'});
+print "<tr> <td valign=top><b>$text{'restore_what'}</b></td> <td>\n";
+foreach $w (@backup_opts) {
+       printf "<input type=checkbox name=what value=%s %s> %s<br>\n",
+               $w, $what{$w} ? "checked" : "", $text{$w."_title"};
+       }
+if (defined(&select_all_link)) {
+       print &select_all_link("what", 0),"\n";
+       print &select_invert_link("what", 0),"\n";
+       }
+print "</td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+print "<input type=submit value='$text{'restore_ok'}'></form>\n";
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+
diff --git a/itsecur-firewall/list_rules.cgi b/itsecur-firewall/list_rules.cgi
new file mode 100755 (executable)
index 0000000..b055ca9
--- /dev/null
@@ -0,0 +1,149 @@
+#!/usr/bin/perl
+# list_rules.cgi
+# Display a list of all active rules
+require './itsecur-lib.pl';
+
+&can_use_error("rules");
+&header($text{'rules_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+
+#                              0-No.   1-Source,2-Destination, 3-Services,     4-Time, 5-Action,       6-Enabled,      7-Comment
+local @CW=(    "5%",   "15%",  "15%",                  "20%",          "5%",           "5%",                   "5%",                   "20%");
+$C_drop="#FFCCcc";
+$C_reject="#FFDDAA";
+$C_accept="";
+$C_disabled="#FF3333";
+$C_separator="#ffffcc";
+
+local $Row_Color="";
+
+@rules = &list_rules();
+@servs = &list_services();
+$edit = &can_edit("rules");
+$times = &supports_time() && &list_times() > 0;
+if (@rules) {
+       if ($edit) {
+               print "<a href='edit_rule.cgi?new=1'>$text{'rules_add'}</a>\n";
+               print "&nbsp;" x 2;
+               print "<a href='edit_sep.cgi?new=1'>$text{'rules_sadd'}</a>\n";
+               print "<br>\n";
+               print "<form action=enable_rules.cgi method=post>\n";
+               }
+       $cols = $times ? 8 : 7;
+       print "<table border>\n";
+       print "<tr $tb> ",
+             "<td width=$CW[0]><b>$text{'rule_num'}</b></td> ",
+             "<td width=$CW[1]><b>$text{'rule_source'}</b></td> ",
+             "<td width=$CW[2]><b>$text{'rule_dest'}</b></td> ",
+             "<td width=$CW[3]><b>$text{'rules_service'}</b></td> ",
+             ($times ? "<td><b>$text{'rule_time'}</b></td> " : ""),
+             "<td width=$CW[5]><b>$text{'rule_action'}</b></td> ",
+             "<td width=$CW[6]><b>$text{'rule_enabled'}</b></td> ",
+             ($config{'show_desc'} ? "<td width=$CW[7]><b>$text{'rules_desc'}</b></td> " :
+                       "<td width=10><b>$text{'rules_move'}</b></td>"),
+             "</tr>\n";
+       foreach $r (@rules) {
+               if ($r->{'sep'}){
+                               $Row_Color="bgcolor=\"$C_separator\" ";                         
+               } elsif (!$r->{'enabled'}){
+                               $Row_Color="bgcolor=\"$C_disabled\" ";
+               } elsif ( $r->{'action'} eq "drop" ){
+                               $Row_Color="bgcolor=\"$C_drop\" ";
+               } elsif ( $r->{'action'} eq "reject" ){
+                               $Row_Color="bgcolor=\"$C_reject\" ";                            
+               } else {
+                          $Row_Color=""; 
+               }
+
+                                       # case('accept') {}
+                                       # case('allow') {}
+                               # case('drop') {}
+                                       #case('reject') {}
+                                       #case('ignore') {}
+                       
+
+                               
+               print "<tr $Row_Color $cb>\n";
+               if ($r->{'sep'}) {
+                       # Actually a separator - just show it's description
+                       print "<td colspan=$cols><b><a href='edit_sep.cgi?idx=$r->{'index'}'>$r->{'desc'}</b></a></td>\n";
+                       }
+               else {
+                       # Show full rule details                        
+
+                       
+                       print "<td width=$CW[0]>";
+                       if ($edit) {
+                               print "<input type=checkbox name=r value=$r->{'index'}>&nbsp;";
+                               }
+                       print "<a href='edit_rule.cgi?",
+                             "idx=$r->{'index'}'>$r->{'num'}</a></td>\n";
+                       print "<td width=$CW[1]>",
+                               &group_names_link($r->{'source'}, 'rules'),
+                               "</td>\n";
+                       print "<td width=$CW[2]>",
+                               &group_names_link($r->{'dest'}, 'rules',
+                                                 &allow_action($r) ? 'dest' : undef),
+                               "</td>\n";
+                       print "<td width=$CW[3]>",&protocol_names($r->{'service'},\@servs),"</td>\n";
+                       if ($times) {
+                               print "<td>",$r->{'time'} eq '*' ?
+                                       $text{'rule_anytime'} :
+                                       $r->{'time'},"</td>\n";
+                               }
+                       print "<td width=$CW[5]>",$text{'rule_'.$r->{'action'}},
+                             $r->{'log'} ? " $text{'rules_log'}" : "","</td>\n";
+                       print "<td width=$CW[6]>",$r->{'enabled'} ? $text{'yes'} :
+                               "<font color=#ff0000>$text{'no'}</font>",
+                             "</td>\n";
+                       if ($config{'show_desc'}) {
+                               print "<td width=$CW[7]>",$r->{'desc'} eq "*" ? "<br>"
+                                                       : $r->{'desc'},"</td>\n";
+                               }
+                       else {
+                               if ($r eq $rules[0] || !$edit) {
+                                       print "<td><img src=images/gap.gif>\n";
+                                       }
+                               else {
+                                       print "<td><a href='up.cgi?idx=$r->{'index'}'>",
+                                             "<img src=images/up.gif border=0></a>\n";
+                                       }
+                               if ($r eq $rules[$#rules] || !$edit) {
+                                       print "<img src=images/gap.gif></td>\n";
+                                       }
+                               else {
+                                       print "<a href='down.cgi?idx=$r->{'index'}'>",
+                                             "<img src=images/down.gif border=0></a></td>\n";
+                                       }
+                               }
+                       }
+               print "</tr>\n";
+               }
+       print "</table>\n";
+       }
+else {
+       print "<b>$text{'rules_none'}</b><p>\n";
+       }
+if ($edit) {
+       print "<a href='edit_rule.cgi?new=1'>$text{'rules_add'}</a>\n";
+       print "&nbsp;" x 2;
+       print "<a href='edit_sep.cgi?new=1'>$text{'rules_sadd'}</a>\n";
+       print "<p>\n";
+       }
+if ($edit && @rules) {
+       print "<input type=submit name=enable value='$text{'rules_enable'}'>\n";
+       print "<input type=submit name=disable value='$text{'rules_disable'}'>\n";
+
+       print "&nbsp;\n";
+       print "<input type=submit name=logon value='$text{'rules_logon'}'>\n";
+       print "<input type=submit name=logoff value='$text{'rules_logoff'}'>\n";
+       print "&nbsp;\n";
+       print "<input type=submit name=delete value='$text{'rules_delete'}'>\n";
+       print "</form>\n";
+       }
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+
diff --git a/itsecur-firewall/list_services.cgi b/itsecur-firewall/list_services.cgi
new file mode 100755 (executable)
index 0000000..582229d
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/perl
+# list_services.cgi
+# Displays a list of standard and custom services
+
+require './itsecur-lib.pl';
+&can_use_error("services");
+&header($text{'services_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+@services = &list_services();
+$edit = &can_edit("services");
+print "<a href='edit_service.cgi?new=1'>$text{'services_add'}</a><br>\n"
+       if ($edit);
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'service_name'}</b></td> ",
+      "<td><b>$text{'service_ports'}</b></td> </tr>\n";
+if (!$services[0]->{'standard'}) {
+       print "<tr $tb> <td colspan=3><b>$text{'services_header1'}</b></td> </tr>\n";
+       }
+foreach $s (@services) {
+       if ($s->{'standard'} && !$doneheader) {
+               print "<tr $tb> <td colspan=3><b>$text{'services_header2'}</b></td> </tr>\n";
+               $doneheader++;
+               }
+       print "<tr $cb>\n";
+       if ($s->{'standard'}) {
+               print "<td>$s->{'name'}</td>\n";
+               }
+       else {
+               print "<td><a href='edit_service.cgi?idx=$s->{'index'}'>",
+                     "$s->{'name'}</a></td>\n";
+               }
+       print "<td>";
+       for($i=0; $i<@{$s->{'protos'}}; $i++) {
+               print &protocol_name($s->{'protos'}->[$i], $s->{'ports'}->[$i]);
+               print "\n";
+               }
+       for($i=0; $i<@{$s->{'others'}}; $i++) {
+               print "<b>$s->{'others'}->[$i]</b>\n";
+               }
+       print "</td>\n";
+       print "</tr>\n";
+       }
+print "</table>\n";
+print "<a href='edit_service.cgi?new=1'>$text{'services_add'}</a><p>\n"
+       if ($edit);
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
diff --git a/itsecur-firewall/list_spoof.cgi b/itsecur-firewall/list_spoof.cgi
new file mode 100755 (executable)
index 0000000..67c9a0b
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+# list_spoof.cgi
+# Show spoofing prevention form
+
+require './itsecur-lib.pl';
+&can_use_error("spoof");
+&header($text{'spoof_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+print "<form action=save_spoof.cgi>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'spoof_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+($iface, @nets) = &get_spoof();
+print "<tr> <td valign=top><b>$text{'spoof_desc'}</b></td> <td>\n";
+printf "<input type=radio name=spoof value=0 %s> %s<br>\n",
+       $iface ? "" : "checked", $text{'spoof_disabled'};
+printf "<input type=radio name=spoof value=1 %s> %s\n",
+       $iface ? "checked" : "", $text{'spoof_enabled'};
+print &iface_input("iface", $iface);
+print "</td> </tr>\n";
+
+print "<tr> <td valign=top><b>$text{'spoof_nets'}</b></td> <td>\n";
+print "<textarea name=nets rows=5 cols=40>",
+       join("\n", @nets),"</textarea></td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+print "<input type=submit value='$text{'save'}'></form>\n";
+&can_edit_disable("spoof");
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
diff --git a/itsecur-firewall/list_syn.cgi b/itsecur-firewall/list_syn.cgi
new file mode 100755 (executable)
index 0000000..d318967
--- /dev/null
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+# list_syn.cgi
+# Show syn attack prevention form
+
+require './itsecur-lib.pl';
+&can_use_error("syn");
+&header($text{'syn_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+print "<form action=save_syn.cgi>\n";
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'syn_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+($flood, $spoof, $fin) = &get_syn();
+
+print "<tr> <td><b>$text{'syn_flood'}</b></td> <td>\n";
+printf "<input type=radio name=flood value=1 %s> %s\n",
+       $flood ? "checked" : "", $text{'yes'};
+printf "<input type=radio name=flood value=0 %s> %s</td> </tr>\n",
+       $flood ? "" : "checked", $text{'no'};
+
+print "<tr> <td><b>$text{'syn_spoof'}</b></td> <td>\n";
+printf "<input type=radio name=spoof value=1 %s> %s\n",
+       $spoof ? "checked" : "", $text{'yes'};
+printf "<input type=radio name=spoof value=0 %s> %s</td> </tr>\n",
+       $spoof ? "" : "checked", $text{'no'};
+
+print "<tr> <td><b>$text{'syn_fin'}</b></td> <td>\n";
+printf "<input type=radio name=fin value=1 %s> %s\n",
+       $fin ? "checked" : "", $text{'yes'};
+printf "<input type=radio name=fin value=0 %s> %s</td> </tr>\n",
+       $fin ? "" : "checked", $text{'no'};
+
+print "</table></td></tr></table>\n";
+print "<input type=submit value='$text{'save'}'></form>\n";
+&can_edit_disable("syn");
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
diff --git a/itsecur-firewall/list_times.cgi b/itsecur-firewall/list_times.cgi
new file mode 100755 (executable)
index 0000000..0091fad
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+# list_times.cgi
+# Display a list of time ranges that can be used in rules
+
+require './itsecur-lib.pl';
+&can_use_error("times");
+&header($text{'times_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+@times = &list_times();
+$edit = &can_edit("times");
+if (@times) {
+       print "<a href='edit_time.cgi?new=1'>$text{'times_add'}</a><br>\n"
+               if ($edit);
+       print "<table border>\n";
+       print "<tr $tb> <td><b>$text{'times_name'}</b></td> ",
+             "<td><b>$text{'times_hours'}</b></td> ",
+             "<td><b>$text{'times_days'}</b></td> </tr>\n";
+       foreach $t (@times) {
+               print "<tr $cb>\n";
+               print "<td><a href='edit_time.cgi?idx=$t->{'index'}'>",
+                     "$t->{'name'}</a></td>\n";
+               print "<td>",$t->{'hours'} eq "*" ? $text{'times_all'} :
+                                                   $t->{'hours'},"</td>\n";
+               print "<td>",$t->{'days'} eq "*" ? $text{'times_all'} :
+                       join(" ", map { $text{'sday_'.$_} } split(/,/, $t->{'days'})),"</td>\n";
+               print "</tr>\n";
+               }
+       print "</table>\n";
+       }
+else {
+       print "<b>$text{'times_none'}</b><p>\n";
+       }
+print "<a href='edit_time.cgi?new=1'>$text{'times_add'}</a><p>\n"
+       if ($edit);
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+
diff --git a/itsecur-firewall/list_users.cgi b/itsecur-firewall/list_users.cgi
new file mode 100755 (executable)
index 0000000..b611f3c
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+# list_users.cgi
+# Show all Webmin users with access to this module only
+
+require './itsecur-lib.pl';
+&foreign_require("acl", "acl-lib.pl");
+&can_use_error("users");
+&header($text{'users_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+# Work out which users have access to this module
+@users = &acl::list_users();
+foreach $u (@users) {
+       @m = @{$u->{'modules'}};
+       if ($u->{'name'} ne "root" &&
+           &indexof($module_name, @m) >= 0) {
+               push(@musers, $u);
+               }
+       }
+
+#print "$text{'users_desc'}<p>\n";
+$edit = &can_edit("users");
+if (@musers) {
+       print "<a href='edit_user.cgi?new=1'>$text{'users_add'}</a><br>\n"
+               if ($edit);
+       print "<table border>\n";
+       print "<tr $tb> ",
+             "<td><b>$text{'users_name'}</b></td> ",
+             "<td><b>$text{'users_ips'}</b></td> ",
+             "<td><b>$text{'users_enabled'}</b></td> ",
+             "</tr>\n";
+       foreach $u (@musers) {
+               print "<tr $cb>\n";
+               print "<td><a href='edit_user.cgi?",
+                     "name=$u->{'name'}'>$u->{'name'}</a></td>\n";
+               print "<td>\n";
+               if ($u->{'allow'}) {
+                       print &text('users_allow', $u->{'allow'});
+                       }
+               elsif ($u->{'deny'}) {
+                       print &text('users_deny', $u->{'deny'});
+                       }
+               else {
+                       print $text{'users_all'};
+                       }
+               print "</td>\n";
+               print "<td>",$u->{'pass'} =~ /^\*LK\*/ ? $text{'rule_no'} : $text{'rule_yes'},"</td>\n";
+               %uaccess = &get_module_acl($u->{'name'});
+               print "</tr>\n";
+               }
+       print "</table>\n";
+       }
+else {
+       print "<b>$text{'users_none'}</b><p>\n";
+       }
+print "<a href='edit_user.cgi?new=1'>$text{'users_add'}</a><p>\n"
+       if ($edit);
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
diff --git a/itsecur-firewall/list_welf.cgi b/itsecur-firewall/list_welf.cgi
new file mode 100755 (executable)
index 0000000..5a9d4ac
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+# Show a form for exporting a log in WELF format
+
+require './itsecur-lib.pl';
+&can_edit_error("report");
+&ReadParse();
+&header($text{'welf_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+print "<form action=welf.cgi/logs.welf method=post>\n";
+foreach $i (keys %in) {
+       print "<input type=hidden name=$i value='",
+             &html_escape($in{$i}),"'>\n";
+       }
+
+print "<table border>\n";
+print "<tr $tb> <td><b>$text{'welf_header'}</b></td> </tr>\n";
+print "<tr $cb> <td><table>\n";
+
+# Show destination
+($mode, @dest) = &parse_backup_dest($config{'welf_dest'});
+print "<tr> <td valign=top><b>$text{'welf_dest'}</b></td>\n";
+print "<td><table cellpadding=0 cellspacing=0>\n";
+printf "<tr> <td><input type=radio name=dest_mode value=0 %s></td> <td>%s</td> </tr>\n",
+       $mode == 0 ? "checked" : "", $text{'backup_dest0'};
+
+printf "<tr> <td><input type=radio name=dest_mode value=1 %s></td> <td>%s</td>\n",
+       $mode == 1 ? "checked" : "", $text{'backup_dest1'};
+printf "<td colspan=3><input name=dest size=40 value='%s'> %s</td> </tr>\n",
+       $mode == 1 ? $dest[0] : "", &file_chooser_button("dest");
+
+printf "<tr> <td><input type=radio name=dest_mode value=3 %s></td> <td>%s</td>\n",
+       $mode == 3 ? "checked" : "", $text{'backup_dest3'};
+printf "<td colspan=3><input name=email size=40 value='%s'></td> </tr>\n",
+       $mode == 3 ? $dest[0] : "";
+
+printf "<tr> <td><input type=radio name=dest_mode value=2 %s></td>\n",
+       $mode == 2 ? "checked" : "";
+printf "<td>%s</td> <td><input name=ftphost size=20 value='%s'></td>\n",
+       $text{'backup_dest2'}, $mode == 2 ? $dest[2] : "";
+printf "<td>%s</td> <td><input name=ftpfile size=20 value='%s'></td> </tr>\n",
+       $text{'backup_ftpfile'}, $mode == 2 ? $dest[3] : "";
+printf "<tr> <td></td> <td>%s</td> <td><input name=ftpuser size=15 value='%s'></td>\n",
+       $text{'backup_ftpuser'}, $mode == 2 ? $dest[0] : "";
+printf "<td>%s</td> <td><input name=ftppass type=password size=15 value='%s'></td> </tr>\n",
+       $text{'backup_ftppass'}, $mode == 2 ? $dest[1] : "";
+print "</table></td> </tr>\n";
+
+print "</table></td></tr></table>\n";
+print "<input type=submit value='$text{'welf_ok'}'></form>\n";
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+
diff --git a/itsecur-firewall/log_parser.pl b/itsecur-firewall/log_parser.pl
new file mode 100644 (file)
index 0000000..839f6b2
--- /dev/null
@@ -0,0 +1,43 @@
+# log_parser.pl
+# Functions for parsing this module's logs
+
+do 'itsecur-lib.pl';
+
+# parse_webmin_log(user, script, action, type, object, &params)
+# Converts logged information from this module into human-readable form
+sub parse_webmin_log
+{
+local ($user, $script, $action, $type, $object, $p) = @_;
+if ($type eq "rule") {
+       local $source = &group_names($p->{'source'});
+       local $dest = &group_names($p->{'dest'});
+       return &text('log_'.$action.'_'.$type, $source, $dest);
+       }
+elsif ($type eq "service" || $type eq "group" || $type eq "user" ||
+       $type eq "time" || $type eq "sep") {
+       return &text('log_'.$action.'_'.$type,
+                    "<tt>".&html_escape($object)."</tt>");
+       }
+elsif ($type eq "nat" || $type eq "pat" || $type eq "spoof") {
+       return $text{'log_'.$action.'_'.$type};
+       }
+elsif ($action eq "backup" || $action eq "restore") {
+       return $object ? &text('log_'.$action, "<tt>".&html_escape($object)."</tt>") : $text{'log_'.$action.'_file'};
+       }
+elsif ($action eq "import") {
+       return $text{'log_import_'.$type};
+       }
+elsif ($type eq "rules") {
+       if (defined($p->{'enabled'})) {
+               return &text('log_oldenable_rules',
+                            $p->{'enabled'}, $p->{'disabled'});
+               }
+       else {
+               return &text('log_'.$action.'_rules', $p->{'count'});
+               }
+       }
+else {
+       return $text{'log_'.$action};
+       }
+}
+
diff --git a/itsecur-firewall/migrate-groups.pl b/itsecur-firewall/migrate-groups.pl
new file mode 100644 (file)
index 0000000..60bb7a1
--- /dev/null
@@ -0,0 +1,23 @@
+$nat_file="/etc/webmin/itsecur-firewall/nat";
+$groups_file="/etc/webmin/itsecur-firewall/groups";
+
+local ($iface, @nets, @maps);
+
+open(NAT, $nat_file) || return ( );
+open(GROUPS, ">>$groups_file");
+
+chop($iface = <NAT>);
+while(<NAT>) {
+       s/\r|\n//g;
+       if (/^(\S+)$/) {
+               }
+       elsif (/^(\S+)\t+(\S+)\t+(\S+)$/) {
+               print GROUPS "$2\t$2\n";
+               }
+       elsif (/^(\S+)\t+(\S+)$/) {
+               print GROUPS "$2\t$2\n";
+               }
+       }
+close(NAT);
+close(GROUPS);
+
diff --git a/itsecur-firewall/module.info b/itsecur-firewall/module.info
new file mode 100644 (file)
index 0000000..e884e96
--- /dev/null
@@ -0,0 +1,5 @@
+desc=Simple Firewall Creator
+os_support=*-linux freebsd
+category=net
+depends=init cron net acl 1.121
+version=2.59
diff --git a/itsecur-firewall/restore.cgi b/itsecur-firewall/restore.cgi
new file mode 100755 (executable)
index 0000000..f7fb1aa
--- /dev/null
@@ -0,0 +1,188 @@
+#!/usr/bin/perl
+# Actually do a restore
+
+require './itsecur-lib.pl';
+&can_edit_error("restore");
+&error_setup($text{'restore_err'});
+&ReadParseMime();
+
+# Validate inputs
+if (!$in{'src_def'}) {
+       if (-d $in{'src'}) {
+               $in{'src'} .= "/firewall.zip";
+               }
+       -r $in{'src'} || &error_cleanup($text{'restore_esrc'});
+       $file = $in{'src'};
+       }
+else {
+       $in{'file'} || &error_cleanup($text{'restore_efile'});
+       $file = &tempname();
+       open(FILE, ">$file");
+       print FILE $in{'file'};
+       close(FILE);
+       }
+if (!$in{'pass_def'}) {
+       $in{'pass'} || &error_cleanup($text{'backup_epass'});
+       }
+@what = split(/\0/, $in{'what'});
+@what || &error_cleanup($text{'restore_ewhat'});
+%what = map { $_, 1 } @what;
+
+# Extract the zip file
+$tempdir = &tempname();
+mkdir($tempdir, 0700);
+$pass = $in{'pass_def'} ? undef : "-P '$in{'pass'}'";
+$out = &backquote_logged("(cd $tempdir && unzip $pass '$file') 2>&1 </dev/null");
+&error_cleanup($text{'restore_epass2'}) if ($? && $out =~ /password/ &&
+                                           $in{'pass_def'});
+&error_cleanup($text{'restore_epass'}) if ($? && $out =~ /password/);
+&error_cleanup($text{'restore_etar'}) if ($?);
+
+# Work out the new state
+@rules = &list_rules(&if_exists("rules"));
+@services = &list_services(&if_exists("services"));
+@groups = &list_groups(&if_exists("groups"));
+($natiface, @nats) = &get_nat(&if_exists("nat"));
+@pats = &get_pat(&if_exists("pat"));
+($spoofiface, @spoofs) = &get_spoof(&if_exists("spoof"));
+($flood, $spoof) = &get_syn(&if_exists("syn"));
+@times = &list_times(&if_exists("times"));
+
+# Ensure that the new state would be consistent
+%groups = map { $_->{'name'}, $_ } @groups;
+%services = map { $_->{'name'}, $_ } @services;
+%times = map { $_->{'name'}, $_ } @times;
+foreach $r (@rules) {
+       foreach $g (split(/\s+/, $r->{'source'}), split(/\s+/, $r->{'dest'})) {
+               if ($g =~ /^\!?\@(.*)$/ && !$groups{$1}) {
+                       push(@cerrs, &text('restore_egroup', "$1",
+                                          $r->{'num'}));
+                       }
+               }
+       foreach $s (split(/,/, $r->{'service'})) {
+               if ($s ne "*" && !$services{$s}) {
+                       push(@cerrs, &text('restore_eservice', $s,
+                                          $r->{'num'}));
+                       }
+               }
+       if ($r->{'time'} ne "*" && !$times{$r->{'time'}}) {
+               push(@cerrs, &text('restore_etime', $r->{'time'},
+                                  $r->{'num'}));
+               }
+       }
+foreach $n (@nats) {
+       if (!ref($n) && $n =~ /^\!?(.*)$/ && !$groups{$1}) {
+               push(@cerrs, &text('restore_enat', $1));
+               }
+       }
+foreach $p (@pats) {
+       if (!$services{$p->{'service'}}) {
+               push(@cerrs, &text('restore_epat', $p->{'service'}));
+               }
+       }
+foreach $n (@nats) {
+       if (!ref($n) && $n =~ /^\!?(.*)$/ && !$groups{$1}) {
+               push(@cerrs, &text('restore_enat', $1));
+               }
+       }
+if (@cerrs) {
+       # Tell the user
+       &header($text{'restore_title'}, "",
+               undef, undef, undef, undef, &apply_button());
+       print "<hr>\n";
+
+       print "<p>$text{'restore_cerr'}<br>\n";
+       print "<ul>\n";
+       foreach $c (@cerrs) {
+               print "<li>$c\n";
+               }
+       print "</ul>\n";
+
+       print "<hr>\n";
+       &footer("", $text{'index_return'});
+       exit;
+       }
+
+# Copy to the config directory
+&automatic_backup();
+&lock_itsecur_files();
+foreach $w (@what) {
+       if ($w eq "ipsec") {
+               # Copy ipsec config to proper location
+               if (&has_ipsec() && -r "$tempdir/ipsec.conf") {
+                       &lock_file($ipsec::config{'file'});
+                       &lock_file($ipsec::config{'secrets'});
+                       system("cp $tempdir/ipsec.conf $ipsec::config{'file'}");
+                       system("cp $tempdir/ipsec.secrets $ipsec::config{'secrets'}");
+                       &unlock_file($ipsec::config{'file'});
+                       &unlock_file($ipsec::config{'secrets'});
+                       }
+               }
+       elsif ($w eq "users") {
+               # Copy Webmin user files
+               &lock_file("$config_directory/miniserv.users");
+               &lock_file("$config_directory/webmin.acl");
+               system("cp $tempdir/miniserv.users $config_directory/miniserv.users");
+               system("cp $tempdir/webmin.acl $config_directory/webmin.acl");
+               foreach $a (glob("$tempdir/*.acl")) {
+                       local $fn = $a;
+                       $fn =~ s/^.*\///;
+                       if ($fn ne "webmin.acl") {
+                               &lock_file("$module_config_directory/$fn");
+                               system("cp $a $module_config_directory/$fn");
+                               &unlock_file("$module_config_directory/$fn");
+                               }
+                       }
+               &unlock_file("$config_directory/miniserv.users");
+               &unlock_file("$config_directory/webmin.acl");
+               &restart_miniserv();
+               }
+       elsif ($w eq "searches") {
+               # Copy searches directory
+               mkdir($searches_directory, 0755);
+               system("cp $tempdir/searches/* $searches_directory >/dev/null 2>&1");
+               }
+       elsif ($w eq "config") {
+               # Update module config - except system type
+               local %newconfig;
+               &read_file("$tempdir/config", \%newconfig);
+               $newconfig{'type'} = $config{'type'};
+               &write_file("$module_config_directory/config", \%newconfig);
+               }
+       else {
+               if (-r "$tempdir/$w") {
+                       system("cp $tempdir/$w $module_config_directory");
+                       }
+               }
+       }
+&unlock_itsecur_files();
+
+# Tell the user
+&header($text{'restore_title'}, "",
+       undef, undef, undef, undef, &apply_button());
+print "<hr>\n";
+
+print "<p>",&text('restore_done'),"<p>\n";
+&cleanup();
+
+print "<hr>\n";
+&footer("", $text{'index_return'});
+&remote_webmin_log("restore", undef, $in{'src_def'} ? undef : $in{'src'});
+
+sub error_cleanup
+{
+&cleanup();
+&error(@_);
+}
+
+sub cleanup
+{
+unlink($file) if ($in{'src_def'});
+system("rm -rf $tempdir") if ($tempdir);
+}
+
+sub if_exists
+{
+return -r "$tempdir/$_[0]" && $what{$_[0]} ? "$tempdir/$_[0]" : undef;
+}
+
diff --git a/itsecur-firewall/save_bandwidth.cgi b/itsecur-firewall/save_bandwidth.cgi
new file mode 100755 (executable)
index 0000000..9b2a464
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/perl
+# Save bandwidth monitoring settings
+
+require './itsecur-lib.pl';
+&can_edit_error("bandwidth");
+&ReadParse();
+&foreign_require("bandwidth", "bandwidth-lib.pl");
+
+if ($in{'enabled'}) {
+       # Enable in config, so that log rules are generated
+       $config{'bandwidth'} = $in{'iface'};
+       &save_module_config();
+
+       # Setup firewall and bandwidth modules
+       $bandwidth::config{'iface'} = $in{'iface'};
+       &bandwidth::save_module_config();
+       $firewall::config{'direct'} = 1;
+       &firewall::save_module_config();
+
+       # Set up syslog.conf entry
+       $conf = &syslog::get_config();
+       $sysconf = &bandwidth::find_sysconf($conf);
+       if (!$sysconf) {
+               &lock_file($syslog::config{'syslog_conf'});
+               &syslog::create_log({ 'file' => $bandwidth::bandwidth_log,
+                                     'active' => 1,
+                                     'sel' => [ "kern.=debug" ] });
+               &unlock_file($syslog::config{'syslog_conf'});
+               $err = &syslog::restart_syslog();
+               &error($err) if ($err);
+               }
+
+       # Set up cron job
+       $job = &bandwidth::find_cron_job();
+       if (!$job) {
+               &cron::create_wrapper($bandwidth::cron_cmd, $bandwidth::module_name, "rotate.pl");
+               $job = { 'user' => 'root',
+                        'active' => 1,
+                        'command' => $bandwidth::cron_cmd,
+                        'special' => 'hourly' };
+               &lock_file(&cron::cron_file($job));
+               &cron::create_cron_job($job);
+               &unlock_file(&cron::cron_file($job));
+               }
+       }
+else {
+       # Disable in config
+       $config{'bandwidth'} = undef;
+       &save_module_config();
+
+       # Remove cron job
+       $job = &bandwidth::find_cron_job();
+       if ($job) {
+               &lock_file(&cron::cron_file($job));
+               &cron::delete_cron_job($job);
+               &unlock_file(&cron::cron_file($job));
+               }
+
+
+       }
+
+&redirect("");
+
diff --git a/itsecur-firewall/save_group.cgi b/itsecur-firewall/save_group.cgi
new file mode 100755 (executable)
index 0000000..eba693e
--- /dev/null
@@ -0,0 +1,145 @@
+#!/usr/bin/perl
+# save_group.cgi
+# Create, update or delete a host group
+
+require './itsecur-lib.pl';
+
+sub check_ip_in_groups{
+  my $my_group;
+}
+
+&can_edit_error("groups");
+&ReadParse();
+@groups = &list_groups();
+if (!$in{'new'}) {
+       $group = $groups[$in{'idx'}];
+       }
+&lock_itsecur_files();
+
+if ($in{'delete'}) {
+       # Check if in use
+       &error_setup($text{'group_err2'});
+       @rules = &list_rules();
+       foreach $r (@rules) {
+               &error($text{'group_einuse'})
+                       if ($r->{'source'} =~ /\@\Q$group->{'name'}\E/ ||
+                           $r->{'dest'} =~ /\@\Q$group->{'name'}\E/);
+               }
+       local @maps;            
+   ($iface, @nets) = &get_nat();
+       @maps = grep { ref($_) } @nets;
+       @nets = grep { !ref($_) } @nets;                
+   
+       local ($net,$local_net);
+       foreach $net (@nets) {
+               $local_net = $net;
+               $local_net =~ s/^\!//;
+               &error($text{'group_in_use_nat'})
+                       if ($local_net eq $group->{'name'} );
+               }
+       local ($m);
+       foreach $m (@maps) {
+               &error($text{'group_in_use_nat'})
+                       if (@$m->[1] eq $group->{'name'} );
+               }
+       
+       local $g;
+       foreach $g (@groups) {
+               next if ($g eq $group);
+               foreach $m (@{$g->{'members'}}) {
+                       
+                       &error($text{'group_in_use_group'}." $g->{name}")
+                                       if ($m eq "\@$group->{'name'}" );
+               }
+       }       
+
+       # Just delete this group
+       splice(@groups, $in{'idx'}, 1);
+       #&automatic_backup();
+       #TODO: Delete from other groups !!
+       }
+else {
+       # Validate inputs
+       &error_setup($text{'group_err'});
+       $in{'name'} =~ /^\S+$/ || &error($text{'group_ename'});
+       if ($in{'new'} || $in{'name'} ne $group->{'name'}) {
+               # Check for clash
+               ($clash) = grep { lc($_->{'name'}) eq lc($in{'name'}) } @groups;
+               $clash && &error($text{'group_eclash'});
+               }
+       for($i=0; defined($in{"member_$i"}); $i++) {
+               next if (!$in{"member_$i"});
+               local $ht = &valid_host($in{"member_$i"});
+               $ht || &error(&text('group_emember', $in{"member_$i"}));
+               if ($ht == 2 && $in{'resolv'}) {
+                       local $rs = &to_ipaddress($in{"member_$i"});
+                       $in{"member_$i"} = $rs if ($rs);
+                       }
+               if ($ht == 4 && $in{"neg_$i"}) {
+                       &error(&text('group_eneg', $in{"member_$i"}));
+                       }
+               push(@members, $in{"neg_$i"}.$in{"member_$i"});
+               }
+       for($i=0; defined($in{"group_$i"}); $i++) {
+               next if (!$in{"group_$i"});
+               $in{"group_$i"} eq $in{'name'} &&
+                       &error($text{'group_eself'});
+               push(@members, "@".$in{"group_$i"});
+               }
+       @members || &error($text{'group_emembers'});
+       $oldname = $group->{'name'};
+       $group->{'name'} = $in{'name'};
+       $group->{'members'} = \@members;
+
+       if ($in{'new'}) {
+               push(@groups, $group);
+               }
+       #@sorted = sort { $a cmp $b } @groups; 
+       #@sorted = sort @groups; 
+        #@groups = @sorted; 
+       if (!$in{'new'} && $oldname ne $group->{'name'}) {
+               # Has been re-named .. update all rules!
+               @rules = &list_rules();
+               foreach $r (@rules) {
+                       $r->{'source'} =~ s/\@\Q$oldname\E$/\@$group->{'name'}/;
+                       $r->{'dest'} =~ s/\@\Q$oldname\E$/\@$group->{'name'}/;
+                       }
+               &save_rules(@rules);
+
+               # And update all other groups
+               foreach $g (@groups) {
+                       next if ($g eq $group);
+                       foreach $m (@{$g->{'members'}}) {
+                               $m = "\@$group->{'name'}"
+                                       if ($m eq "\@$oldname");
+                               }
+                       }
+               local @maps;            
+               ($iface, @nets) = &get_nat();
+               @maps = grep { ref($_) } @nets;
+               @nets = grep { !ref($_) } @nets;                
+               local ($m,$net);
+
+               foreach $net (@nets) {
+                       if ($net eq "$oldname") {
+                               $net = "$group->{'name'}";
+                               } elsif ($net eq "!$oldname") {
+                          $net = "!$group->{'name'}";
+                               }
+                       }
+               foreach $m (@maps) {
+                       if (@$m->[1] eq "$oldname") {
+                               @$m->[1] = "$group->{'name'}";
+                               } 
+                       }                       
+               &save_nat($iface, @nets, @maps);      
+               }
+       }
+
+&save_groups(@groups);
+$from = $in{'from'} || "groups";
+&unlock_itsecur_files();
+&remote_webmin_log($in{'delete'} ? "delete" : $in{'new'} ? "create" : "update",
+           "group", $group->{'name'}, $group);
+&redirect("list_${from}.cgi");
+
diff --git a/itsecur-firewall/save_nat.cgi b/itsecur-firewall/save_nat.cgi
new file mode 100755 (executable)
index 0000000..620f80d
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+# save_nat.cgi
+# Update NAT setting
+
+require './itsecur-lib.pl';
+&can_edit_error("nat");
+&ReadParse();
+&lock_itsecur_files();
+
+&error_setup($text{'nat_err'});
+if ($in{'nat'}) {
+       $iface = $in{'iface'} || $in{'iface_other'};
+       $iface =~ /^[a-z0-9:\.]+$/ || &error($text{'nat_eiface'});
+       }
+for($i=0; defined($n = $in{"net_$i"}); $i++) {
+       push(@nets, $n) if ($n);
+       }
+for($i=0; defined($n = $in{"excl_$i"}); $i++) {
+       push(@nets, "!$n") if ($n);
+       }
+local @dests;
+for($i=0; defined($e = $in{"ext_$i"}); $i++) {
+       next if (!$e);  
+       #gethostbyname($e) || &error(&text('nat_eext', $i+1));
+       valid_host($e) || &error(&text('nat_eext', $i+1));
+       #is_one_host("\@$e") && &error(&text('nat_eext', $i+1));        
+       $n = $in{"int_$i"};
+       #gethostbyname($n) || &error(&text('nat_eint', $i+1));
+       ##valid_host($n) || &error(&text('nat_eint', $i+1));    
+       is_one_host("\@$n") && &error(&text('nat_eint', $i+1));         
+       $v = $in{"virt_$i"};
+       push(@maps, [ $e, $n, $v ? ( $v ) : ( ) ]);
+       }
+&automatic_backup();
+&save_nat($iface, @nets, @maps);
+&unlock_itsecur_files();
+&remote_webmin_log("update", "nat");
+&redirect("");
+
diff --git a/itsecur-firewall/save_nat2.cgi b/itsecur-firewall/save_nat2.cgi
new file mode 100755 (executable)
index 0000000..71ed606
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+# save_nat.cgi
+# Update NAT setting
+
+require './itsecur-lib.pl';
+&can_edit_error("nat");
+&ReadParse();
+&lock_itsecur_files();
+
+&error_setup($text{'nat_err'});
+if ($in{'nat'}) {
+       $iface = $in{'iface'} || $in{'iface_other'};
+       $iface =~ /^[a-z0-9:\.]+$/ || &error($text{'nat_eiface'});
+       }
+for($i=0; defined($n = $in{"net_$i"}); $i++) {
+       push(@nets, $n) if ($n);
+       }
+for($i=0; defined($n = $in{"excl_$i"}); $i++) {
+       push(@nets, "!$n") if ($n);
+       }
+for($i=0; defined($e = $in{"ext_$i"}); $i++) {
+       next if (!$e);
+       gethostbyname($e) || &error(&text('nat_eext', $i+1));
+       $n = $in{"int_$i"};
+       gethostbyname($n) || &error(&text('nat_eint', $i+1));
+       $v = $in{"virt_$i"};
+       push(@maps, [ $e, $n, $v ? ( $v ) : ( ) ]);
+       }
+&automatic_backup();
+&save_nat2($iface, @nets, @maps);
+&unlock_itsecur_files();
+&remote_webmin_log("update", "nat2");
+&redirect("");
+
diff --git a/itsecur-firewall/save_pat.cgi b/itsecur-firewall/save_pat.cgi
new file mode 100755 (executable)
index 0000000..b016b6a
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+# save_pat.cgi
+# Save incoming forwarded ports
+
+require './itsecur-lib.pl';
+&can_edit_error("pat");
+&ReadParse();
+&lock_itsecur_files();
+
+&error_setup($text{'pat_err'});
+for($i=0; defined($s = $in{"service_$i"}); $i++) {
+       next if (!$s);
+       $h = $in{"host_$i"};
+       gethostbyname($h) || &error(&text('pat_ehost', $i+1));
+       $iface = $in{"iface_$i"};
+       $iface eq "" || $iface =~ /^[a-z0-9:\.]+$/ ||
+               &error(&text('pat_eiface', $i+1));
+       push(@forwards, { 'service' => $s,
+                         'host' => $h,
+                         'iface' => $iface });
+       }
+
+&automatic_backup();
+&save_pat(@forwards);
+&unlock_itsecur_files();
+&remote_webmin_log("update", "pat");
+&redirect("");
+
diff --git a/itsecur-firewall/save_remote.cgi b/itsecur-firewall/save_remote.cgi
new file mode 100755 (executable)
index 0000000..79a7a47
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+# Set up remote logging, creating a webmin server if needed
+
+require './itsecur-lib.pl';
+&foreign_require("servers", "servers-lib.pl");
+&can_edit_error("remote");
+&ReadParse();
+
+# Validate inputs
+&error_setup($text{'remote_err'});
+if (!$in{'host_def'}) {
+       gethostbyname($in{'host'}) || &error($text{'remote_ehost'});
+       $in{'user'} || &error($text{'remote_euser'});
+       $server = &save_remote($in{'host'}, $in{'port'},
+                              $in{'user'}, $in{'pass'}, 1, 1);
+       }
+else {
+       # Just stop logging
+       &save_remote(undef, undef, undef, undef, 0, 1);
+       }
+&webmin_log("remote");
+&redirect("");
+
+
diff --git a/itsecur-firewall/save_rule.cgi b/itsecur-firewall/save_rule.cgi
new file mode 100755 (executable)
index 0000000..0442466
--- /dev/null
@@ -0,0 +1,124 @@
+#!/usr/bin/perl
+# save_rule.cgi
+# Create, update or delete a firewall rule
+
+require './itsecur-lib.pl';
+&can_edit_error("rules");
+&ReadParse();
+@rules = &list_rules();
+@groups = &list_groups();
+if (!$in{'new'}) {
+       $rule = $rules[$in{'idx'}];
+       }
+&lock_itsecur_files();
+
+if ($config{'rusure'} && !$in{'confirm'} && !$in{'new'}) {
+       # Ask for confirmation before making this change
+       &header($text{'rule_title2'}, "",
+               undef, undef, undef, undef, &apply_button());
+       $rule = $rules[$in{'idx'}];
+       print "<hr>\n";
+
+       print "<form action=save_rule.cgi>\n";
+       print "<center>",&text($in{'delete'} ? 'rule_rusured'
+                                            : 'rule_rusures'),"<p>\n";
+       foreach $i (keys %in) {
+               foreach $v (split(/\0/, $in{$i})) {
+                       print "<input type=hidden name=$i value='",
+                               &html_escape($v),"'>\n";
+                       }
+               }
+       print "<input type=submit name=confirm value='$text{'rule_goahead'}'>\n";
+       print "</center></form>\n";
+
+       print "<hr>\n";
+       &footer("list_rules.cgi", $text{'rules_return'});
+       exit;
+       }
+
+if ($in{'delete'}) {
+       # Just take out rule
+       splice(@rules, $in{'idx'}, 1);
+       }
+else {
+       # Validate and store inputs
+       &error_setup($text{'rule_err'});
+       $rule->{'desc'} = $in{'desc'} || "*";
+       foreach $s ('source', 'dest') {
+               if ($in{"${s}_mode"} == 0) {
+                       $rule->{$s} = "*";
+                       }
+               elsif ($in{"${s}_mode"} == 1) {
+                       &valid_host($in{"${s}_host"}) ||
+                           &error($text{"rule_e${s}"});
+                       if ($in{"${s}_resolv"}) {
+                               local $rs = &to_ipaddress($in{"${s}_host"});
+                               $in{"${s}_host"} = $rs if ($rs);
+                               }
+                       if ($in{"${s}_name"}) {
+                               # Add a group for this network/host
+                               $in{"${s}_name"} =~ /^\S+$/ ||
+                                       &error($text{'rule_ename'});
+                               $rule->{$s} = "@".$in{"${s}_name"};
+                               local @mems = ( $in{"${s}_host"} );
+                               push(@groups, { 'name' => $in{"${s}_name"},
+                                               'members' => \@mems });
+                               }
+                       else {
+                               $rule->{$s} = $in{"${s}_host"};
+                               }
+                       }
+               elsif ($in{"${s}_mode"} == 2) {
+                       $rule->{$s} = join(" ", map { '@'.$_ }
+                                          split(/\0/, $in{"${s}_group"}));
+                       $rule->{$s} || &error($text{'rule_egroups'});
+                       }
+               elsif ($in{"${s}_mode"} == 3) {
+                       $rule->{$s} = '%'.$in{"${s}_iface"};
+                       }
+               $rule->{$s} = "!".$rule->{$s} if ($in{"${s}_not"});
+               }
+       if ($in{"service_mode"} == 0) {
+               $rule->{'service'} = "*";
+               }
+       else {
+               $rule->{'service'} = join(",", split(/\0/, $in{"service"}));
+               $rule->{'service'} || &error($text{'rule_eservices'});
+               }
+       $rule->{'service'} = "!".$rule->{'service'} if ($in{'snot'});
+       $rule->{'action'} = $in{'action'};
+       $rule->{'log'} = int($in{'log'});
+       $rule->{'time'} = $in{'time_def'} ? "*" : $in{'time'};
+       $rule->{'enabled'} = $in{'enabled'};
+
+       if ($in{'new'}) {
+               # Add to list at chosen position
+               if ($in{'pos'} == -1) {
+                       push(@rules, $rule);
+                       }
+               else {
+                       splice(@rules, $in{'pos'}, 0, $rule);
+                       }
+               }
+       else {
+               # Maybe change position
+               foreach $r (grep { $_ ne $rule } @rules) {
+                       if ($r->{'index'} == $in{'pos'}) {
+                               push(@newrules, $rule);
+                               }
+                       push(@newrules, $r);
+                       }
+               push(@newrules, $rule) if ($in{'pos'} == -1);
+               @rules = @newrules;
+               }
+       }
+
+# Save rules list
+&automatic_backup();
+&save_rules(@rules);
+&save_groups(@groups);
+&unlock_itsecur_files();
+&remote_webmin_log($in{'delete'} ? "delete" : $in{'new'} ? "create" : "update",
+           "rule", $rule->{'index'}+1, $rule);
+&redirect("list_rules.cgi");
+
diff --git a/itsecur-firewall/save_search.cgi b/itsecur-firewall/save_search.cgi
new file mode 100755 (executable)
index 0000000..ed5ad0b
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+# Save the current search
+
+require './itsecur-lib.pl';
+&can_edit_error("report");
+&ReadParse();
+
+# Validate inputs
+$in{'save_name'} =~ /\S/ && $in{'save_name'} !~ /\.\./ ||
+       &error($text{'report_esave'});
+%search = ( 'save_name', $in{'save_name'} );
+foreach $f (@search_fields) {
+       foreach $i (keys %in) {
+               if ($i =~ /^\Q$f\E_/) {
+                       $search{$i} = $in{$i};
+                       }
+               }
+       }
+&save_search(\%search);
+&redirect("list_report.cgi?save_name=".&urlize($in{'save_name'}));
+
diff --git a/itsecur-firewall/save_sep.cgi b/itsecur-firewall/save_sep.cgi
new file mode 100755 (executable)
index 0000000..817e9fd
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+# Create, update or delete a rules section separator
+
+require './itsecur-lib.pl';
+&can_edit_error("rules");
+&ReadParse();
+@rules = &list_rules();
+if (!$in{'new'}) {
+       $rule = $rules[$in{'idx'}];
+       }
+&lock_itsecur_files();
+
+if ($in{'delete'}) {
+       # Just take out rule
+       splice(@rules, $in{'idx'}, 1);
+       }
+else {
+       # Validate and store inputs
+       &error_setup($text{'sep_err'});
+       $in{'desc'} || &error($text{'sep_edesc'});
+       $rule->{'desc'} = $in{'desc'};
+       $rule->{'sep'} = 1;
+
+       if ($in{'new'}) {
+               # Add to list at chosen position
+               if ($in{'pos'} == -1) {
+                       push(@rules, $rule);
+                       }
+               else {
+                       splice(@rules, $in{'pos'}, 0, $rule);
+                       }
+               }
+       else {
+               # Maybe change position
+               foreach $r (grep { $_ ne $rule } @rules) {
+                       if ($r->{'index'} == $in{'pos'}) {
+                               push(@newrules, $rule);
+                               }
+                       push(@newrules, $r);
+                       }
+               push(@newrules, $rule) if ($in{'pos'} == -1);
+               @rules = @newrules;
+               }
+       }
+
+# Save rules list
+&save_rules(@rules);
+&unlock_itsecur_files();
+&remote_webmin_log($in{'delete'} ? "delete" : $in{'new'} ? "create" : "update",
+           "sep", $rule->{'index'}+1, $rule);
+&redirect("list_rules.cgi");
+
diff --git a/itsecur-firewall/save_service.cgi b/itsecur-firewall/save_service.cgi
new file mode 100755 (executable)
index 0000000..b412693
--- /dev/null
@@ -0,0 +1,117 @@
+#!/usr/bin/perl
+# save_service.cgi
+# Create, update or delete a service
+
+require './itsecur-lib.pl';
+&can_edit_error("services");
+&ReadParse();
+&lock_itsecur_files();
+@servs = &list_services();
+if (!$in{'new'}) {
+       $serv = $servs[$in{'idx'}];
+       }
+
+if ($in{'delete'}) {
+       # Check if in use by a rule or other service
+       &error_setup($text{'service_err2'});
+       @rules = &list_rules();
+       foreach $r (@rules) {
+               @rservs = split(/,/, $r->{'service'});
+               &error($text{'service_einuse'})
+                       if (&indexof($serv->{'name'}, @rservs) >= 0);
+               }
+       foreach $s (@servs) {
+               &error($text{'service_einuse2'})
+                       if (&indexof($serv->{'name'}, @{$s->{'others'}}) >= 0);
+               }
+
+       # Just delete this service
+       splice(@servs, $in{'idx'}, 1);
+       &automatic_backup();
+       }
+else {
+       # Validate inputs
+       &error_setup($text{'service_err'});
+       $in{'name'} =~ /\S/ || &error($text{'service_ename'});
+       @others = split(/\0/, $in{'others'});
+       for($i=0; defined($in{"proto_$i"}); $i++) {
+               next if (!$in{"proto_$i"});
+               if ($in{"proto_$i"} eq 'icmp') {
+                       $in{"port_$i"} =~ /^\d+$/ ||
+                          $in{"port_$i"} eq '*' ||
+                               &error(&text('service_eicmp', $i+1));
+                       }
+               elsif ($in{"proto_$i"} eq 'ip') {
+                       $in{"port_$i"} =~ /^\d+$/ ||
+                               &error(&text('service_eip', $i+1));
+                       }
+               else {
+                       $in{"port_$i"} =~ /^\d+$/ ||
+                          $in{"port_$i"} =~ /^\d+\-\d+$/ ||
+                          $in{"port_$i"} =~ /^\d+(\s+\d+)*$/ ||
+                               &error(&text('service_eport', $i+1));
+                       }
+               push(@protos, $in{"proto_$i"});
+               push(@ports, $in{"port_$i"});
+               }
+       @protos || @others || &error($text{'service_enone'});
+       #&unique(@protos) == 1 || &error($text{'service_eprotos'});
+       if ($in{'new'} || lc($in{'name'}) ne lc($serv->{'name'})) {
+               # Check for clash
+               ($clash) = grep { lc($_->{'name'}) eq lc($in{'name'}) } @servs;
+               $clash && &error($text{'service_eclash'});
+               }
+       $oldname = $serv->{'name'};
+       $serv->{'name'} = $in{'name'};
+       $serv->{'protos'} = \@protos;
+       $serv->{'ports'} = \@ports;
+       $serv->{'others'} = \@others;
+
+       if ($in{'new'}) {
+               push(@servs, $serv);
+               }
+
+       &automatic_backup();
+       if (!$in{'new'} && $oldname ne $serv->{'name'}) {
+               # Has been re-named .. update all rules!
+               @rules = &list_rules();
+               foreach $r (@rules) {
+                       $r->{'service'} = &replace_service_name(
+                               $r->{'service'}, $oldname, $serv->{'name'});
+                       }
+               &save_rules(@rules);
+
+               # Also update PAT services
+               @forwards = &get_pat();
+               foreach $f (@forwards) {
+                       $f->{'service'} = &replace_service_name(
+                               $f->{'service'}, $oldname, $serv->{'name'});
+                       }
+               &save_pat(@forwards);
+
+               # Also update other services
+               foreach $s (@servs) {
+                       $idx = &indexof($oldname, @{$s->{'others'}});
+                       if ($idx >= 0) {
+                               $s->{'others'}->[$idx] = $serv->{'name'};
+                               }
+                       }
+               }
+       }
+
+&save_services(@servs);
+&unlock_itsecur_files();
+&remote_webmin_log($in{'delete'} ? "delete" : $in{'new'} ? "create" : "update",
+           "service", $serv->{'name'}, $serv);
+&redirect("list_services.cgi");
+
+# replace_service_name(comma-list, old, new)
+sub replace_service_name
+{
+local @servs = split(/,/, $_[0]);
+foreach $s (@servs) {
+       $s = $_[2] if ($s eq $_[1]);
+       }
+return join(",", @servs);
+}
+
diff --git a/itsecur-firewall/save_spoof.cgi b/itsecur-firewall/save_spoof.cgi
new file mode 100755 (executable)
index 0000000..057ec87
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+# save_spoof.cgi
+# Save spoofing settings
+
+require './itsecur-lib.pl';
+&can_edit_error("spoof");
+&lock_itsecur_files();
+&ReadParse();
+
+&error_setup($text{'spoof_err'});
+if ($in{'spoof'}) {
+       $iface = $in{'iface'} || $in{'iface_other'};
+       $iface =~ /^[a-z0-9:\.]+$/ || &error($text{'nat_eiface'});
+       }
+else {
+       }
+@nets = split(/\s+/, $in{'nets'});
+foreach $n (@nets) {
+       $n =~ /^([0-9\.]+)\/(\d+)$/ &&
+               $2 >= 0 && $2 <= 32 &&
+               &check_ipaddress("$1") ||
+                       &error(&text('spoof_enet', $n));
+       }
+!$iface || @nets || &error($text{'spoof_enets'});
+&automatic_backup();
+&save_spoof($iface, @nets);
+&unlock_itsecur_files();
+&remote_webmin_log("update", "spoof");
+&redirect("");
+
diff --git a/itsecur-firewall/save_syn.cgi b/itsecur-firewall/save_syn.cgi
new file mode 100755 (executable)
index 0000000..096a816
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/perl
+# save_syn.cgi
+# Save syn settings
+
+require './itsecur-lib.pl';
+&can_edit_error("syn");
+&lock_itsecur_files();
+&ReadParse();
+
+&error_setup($text{'syn_err'});
+$flood = $in{'flood'};
+$spoof = $in{'spoof'};
+$fin = $in{'fin'};
+&automatic_backup();
+&save_syn($flood, $spoof, $fin);
+&unlock_itsecur_files();
+&remote_webmin_log("update", "syn");
+&redirect("");
+
diff --git a/itsecur-firewall/save_time.cgi b/itsecur-firewall/save_time.cgi
new file mode 100755 (executable)
index 0000000..4fbd860
--- /dev/null
@@ -0,0 +1,77 @@
+#!/usr/bin/perl
+# save_time.cgi
+# Create, update or delete a time range
+
+require './itsecur-lib.pl';
+&can_edit_error("times");
+&lock_itsecur_files();
+&ReadParse();
+@times = &list_times();
+if (!$in{'new'}) {
+       $time = $times[$in{'idx'}];
+       }
+
+if ($in{'delete'}) {
+       # Check if in use
+       &error_setup($text{'time_err2'});
+       @rules = &list_rules();
+       foreach $r (@rules) {
+               &error($text{'time_einuse'})
+                       if ($r->{'time'} eq $time->{'name'});
+               }
+
+       # Just delete this time
+       splice(@times, $in{'idx'}, 1);
+       &automatic_backup();
+       }
+else {
+       # Validate inputs
+       &error_setup($text{'time_err'});
+       $in{'name'} =~ /^\S+$/ || &error($text{'time_ename'});
+       if ($in{'new'} || $in{'name'} ne $time->{'name'}) {
+               # Check for clash
+               ($clash) = grep { lc($_->{'name'}) eq lc($in{'name'}) } @times;
+               $clash && &error($text{'time_eclash'});
+               }
+       if (!$in{'hours_def'}) {
+               foreach $t ('from', 'to') {
+                       $tm = $in{$t};
+                       $tm =~ /^(\d+):(\d+)$/ || &error($text{'time_e'.$t});
+                       $1 >= 0 && $1 < 24 || &error($text{'time_ehour'.$t});
+                       $2 >= 0 && $2 < 60 || &error($text{'time_emin'.$t});
+                       }
+               }
+       if (!$in{'days_def'}) {
+               @days = split(/\0/, $in{'days'});
+               @days || &error($text{'time_edays'});
+               }
+       $oldname = $time->{'name'};
+       $time->{'name'} = $in{'name'};
+       $time->{'hours'} = $in{'hours_def'} ? "*" :
+                               $in{'from'}."-".$in{'to'};
+       $time->{'days'} = $in{'days_def'} ? "*" :
+                               join(",", @days);
+
+       if ($in{'new'}) {
+               push(@times, $time);
+               }
+
+       &automatic_backup();
+       if (!$in{'new'} && $oldname ne $time->{'name'}) {
+               # Has been re-named .. update all rules!
+               @rules = &list_rules();
+               foreach $r (@rules) {
+                       if ($r->{'time'} eq $oldname) {
+                               $r->{'time'} = $time->{'name'};
+                               }
+                       }
+               &save_rules(@rules);
+               }
+       }
+
+&save_times(@times);
+&unlock_itsecur_files();
+&remote_webmin_log($in{'delete'} ? "delete" : $in{'new'} ? "create" : "update",
+           "time", $time->{'name'}, $time);
+&redirect("list_times.cgi");
+
diff --git a/itsecur-firewall/save_user.cgi b/itsecur-firewall/save_user.cgi
new file mode 100755 (executable)
index 0000000..efc52be
--- /dev/null
@@ -0,0 +1,116 @@
+#!/usr/bin/perl
+# save_user.cgi
+# Create, update or delete a Webmin user
+
+require './itsecur-lib.pl';
+&foreign_require("acl", "acl-lib.pl");
+&can_edit_error("users");
+&ReadParse();
+&lock_itsecur_files();
+@users = &acl::list_users();
+if (!$in{'new'}) {
+       ($user) = grep { $_->{'name'} eq $in{'old'} } @users;
+       }
+
+if ($in{'delete'}) {
+       # Delete him
+       &automatic_backup();
+       &acl::delete_user($user->{'name'});
+       }
+else {
+       # Validate and store inputs
+       &error_setup($text{'user_err'});
+       $in{'name'} || &error($text{'user_ename'});
+       $in{'name'} =~ /^[A-z0-9\-\_\.]+$/ ||
+               &error(&acl::text('save_ename', $in{'name'}));
+       $in{'name'} eq 'webmin' && &error($acl::text{'save_enamewebmin'});
+       if (!$in{'old'} || $in{'old'} ne $in{'name'}) {
+               foreach $u (@users, &acl::list_groups()) {
+                       if ($u->{'name'} eq $in{'name'}) {
+                               &error(&acl::text('save_edup', $in{'name'}));
+                               }
+                       }
+               }
+       $user->{'name'} = $in{'name'};
+       if (!$in{'same'}) {
+               if (defined(&acl::encrypt_password)) {
+                       $user->{'pass'} = &acl::encrypt_password($in{'pass'});
+                       }
+               else {
+                       $salt = substr(time(), -8);
+                       $user->{'pass'} = crypt($in{'pass'}, $salt);
+                       }
+               }
+       $locked = ($user->{'pass'} =~ /^\*LK\*/);
+       if ($in{'enabled'} && $locked) {
+               $user->{'pass'} = substr($user->{'pass'}, 4);
+               }
+       elsif (!$in{'enabled'} && !$locked) {
+               $user->{'pass'} = "*LK*".$user->{'pass'};
+               }
+
+       # Validate and save IPs
+       if ($in{'ipmode'}) {
+               @hosts = split(/\s+/, $in{"ips"});
+               if (!@hosts) { &error($acl::text{'save_enone'}); }
+               foreach $h (@hosts) {
+                       if ($h =~ /^([0-9\.]+)\/([0-9\.]+)$/) {
+                               &check_ipaddress($1) ||
+                                       &error(&acl::text('save_enet', $1));
+                               &check_ipaddress($2) ||
+                                       &error(&acl::text('save_emask', $2));
+                               $i = $h;
+                               }
+                       elsif ($h =~ /^[0-9\.]+$/) {
+                               &check_ipaddress($h) ||
+                                       &error(&acl::text('save_eip', $h));
+                               $i = $h;
+                               }
+                       elsif ($h =~ /^\*\.(\S+)$/) {
+                               $i = $h;
+                               }
+                       elsif ($h eq 'LOCAL') {
+                               $i = 'LOCAL';
+                               }
+                       elsif (!($i = join('.',unpack("CCCC",inet_aton($h))))) {
+                               &error(&acl::text('save_ehost', $h));
+                               }
+                       push(@ips, $i);
+                       }
+               }
+       delete($user->{'allow'});
+       delete($user->{'deny'});
+       if ($in{'ipmode'} == 1) {
+               $user->{'allow'} = join(" ", @ips);
+               }
+       elsif ($in{'ipmode'} == 2) {
+               $user->{'deny'} = join(" ", @ips);
+               }
+
+       &automatic_backup();
+
+       $user->{'modules'} = [ split(/\0/, $in{'mods'}) ];
+       if ($in{'new'}) {
+               # Create the user
+               &acl::create_user($user);
+               }
+       else {
+               # Modify the user
+               &acl::modify_user($in{'old'}, $user);
+               }
+
+       # Update his ACL
+       require "./acl_security.pl";
+       %uaccess = &get_module_acl($in{'name'});
+       &acl_security_save(\%uaccess);
+       if ($in{'new'}) {
+               $uaccess{'noconfig'} = 1;
+               }
+       &save_module_acl(\%uaccess, $in{'name'});
+       }
+&acl::restart_miniserv();
+&unlock_itsecur_files();
+&remote_webmin_log($in{'delete'} ? "delete" : $in{'new'} ? "create" : "update",
+           "user", $user->{'name'}, $user);
+&redirect("list_users.cgi");
+
diff --git a/itsecur-firewall/standard-services b/itsecur-firewall/standard-services
new file mode 100644 (file)
index 0000000..193a29f
--- /dev/null
@@ -0,0 +1,223 @@
+AOL                    TCP     5190            #AOL Instant Messanger
+AP-Defender            TCP     2626            #Defender Authentication Service
+BackDoor-Setup         TCP     5000            #Use by Boinet Lite, Blazer 5, Bubbel Trojans
+Backage                        TCP      411            #Backage Trojan
+BackDoor-G             TCP     1243            #Also used by Subseven, Tiles Trojans
+Citrix-ICA             TCP     1494            #ICA Gerneral Service
+Connect-BackDoor       TCP     4000            #Also used by SkyDance Trojan
+CrackDown              TCP     4444            #Crackdown Trojan
+DaCryptic              TCP     1074            #DaCryptic Trojan
+DameWare               TCP     6129            #DameWare Mini Remote Control Protocol
+DayTime                        TCP       13    UDP     13              #DayTime Server Protocol
+DerSphere              TCP     1000            #Also used by Insane Network Trojans
+DerSphere-II           TCP     2000            #Also used by SennaSpy Trojan Generator
+Direct-Connect         TCP      411-412        #Direct Connect P2P Application
+Discard                        TCP        9            #Discard Server Protocol
+DNS                    TCP       53    UDP     53              #Domain Name Service
+Echo                   TCP        7            #Echo Protocol
+eDonkey-4661           TCP     4661            #eDonkey Protocol
+eDonkey-4662           TCP     4662            #eDonkey Protocol
+nTrust-Admin           TCP      710            #nTrust CA Administration Service
+nTrust-Key-Mgmt                TCP      709            #nTrust Key Management Service
+ERP-1                  TCP     12345-12349     #ERP Service
+Exec                   TCP      512            #Remote Execution
+Finger                 TCP       79            #UNIX Finger Protocol
+Freak2K                        TCP     7001            #Also used by Freak88, NetSnooper Gold Trojans
+FTP                    TCP       21            #File Transfer Protocol
+GateCrasher            TCP     6970            #GateCrasher Trojan
+GNUtella-RTR           TCP     6347    UDP     6347            #Also used by BearShare, ToadNode, LimeWire
+GNUtella               TCP     6346    UDP     6346            #Also used by BearShare, ToadNode, LimeWire
+Gopher                 TCP       70            #Internet Gopher Protocol
+GoToMyPC               TCP     8200            #GotoMy PC
+H323                   TCP     1720            #Video Conference Transmission over IP
+HackaTACK-31785                TCP     31785           #HackAttack
+HackaTACK-31787                TCP     31787           #HackAttack                             
+HackaTACK-31788                TCP     31788           #HackAttack
+HackaTACK-31790                TCP     31790           #HackAttack
+HackaTACK-31792                TCP     31792           #HackAttack
+HotLine-Client         TCP     5500-5503       #HotLine Client Connection
+HTTP                   TCP       80            #Hyper-Text Transfer Protocol
+HTTPS                  TCP      443            #TLS/SSL
+ICKiller               TCP     1027            #ICKiller
+IDENT                  TCP      113            #Identify RCS Keyword String in Files
+IKE                    TCP      500    UDP     500             #IPSec Internet Key Exchange
+IMAP                   TCP      143            #Interactive Mail Protocol
+iMesh                  TCP     5000            #Also used by many Trojans and UPNP Service
+InCommand              TCP     1029            #Also used by ICQ Nuke 98 Trojan
+IPSO-CMP               TCP     1111            #IPSO Clustering Management Protocol
+IRC-1                  TCP     6660-6670       #Internet Relay Chat
+IRC-2                  TCP     7000            #Internet Relay Chat
+Jade                   TCP     1024            #Also used by Latinus, NetSPY, Rat Trojans
+Kaos                   TCP     1212            #Kaos Trojan
+KazzaA                 TCP     1214            #Fast Track P2P Protocol
+Kerberos-v5            TCP       88    UDP     88              #Kerberos Authentication Version 5
+Kerberos               TCP      750    UDP     750             #Kerberos Authentication
+Kuang2                 TCP     17300           #Kuang2 Trojan
+LDAP                   TCP      389            #Lightweight Directory Access Protocol
+LDAP-SSL               TCP      636            #Lightweight Directory Access Protocol over TLS/SSL
+Rlogin                 TCP      513            #Rlogin
+iNotes                 TCP     1352            #Lotus iNotes Web Access Protocol
+lpdw0rm                        TCP      515            #Used by Ramen Trojans and Printer Service
+Madster                        TCP     5025            #Formally called Aimster
+####Microsoft-TS               TCP      445            #Microsoft CIFS's over TCP
+Mneah                  TCP     4666            #The Mneah Trojan
+MSN-File Transfer      TCP     6891-6900       #MSN File Tranfer
+MSN-Messenger          TCP     1863            #MSN Instant Messenger
+MSSQL-Mon              TCP     1434            #MSSQL Monitor
+MSSQL                  TCP     1433            #MSSQL Server
+mysql                  TCP     3306            #MySQL Server   
+MultiDropper           TCP     1035            #MultiDropper Trojan
+Napster-Client         TCP     6600-6699       #Napster Client also used by WinMX
+Napster-Dir4           TCP     4444            #Napster Directory Connections
+Napster-Dir5           TCP     5555            #Napster Directory Connections
+Napster-Dir6           TCP     6666            #Napster Directory Connections
+Napster-Dir7           TCP     7777            #Napster Directory Connections
+Napster-Dir8           TCP     8888            #Napster Directory Connections
+Napster-Redirector     TCP     8875            #Napster Redirector
+NBSession              TCP      139            #NetBios Session Service
+NCP                    TCP      524            #Novell Netware Core Protocol
+NetShow                        TCP     1755            #Microsoft NetShow-Windows Media Player
+NetSTAT                        TCP       15            #UNIX NetSTAT Protocol
+NFSD                   TCP     2049    UDP     2049            #Network File System Deamon
+NNTP                   TCP      119            #Network News Tranfer Protocol
+NTP                    TCP      123    UDP     123             #Network Time Protocol
+OAS-NameServer         TCP     2649            #Oracle Application Server
+OAS-ORB                        TCP     2651            #Oracle Application Server
+OpenWindows            TCP     2000            #Open Windows
+####Orbix-1570         TCP     1570            #IONA Orbix Deamon
+####Orbix-1571         TCP     1571            #IONA Orbix Deamon
+PcAnywhere-Date                TCP     5631            #pcAnywhere
+PcTele-FileSync                TCP     2299            #Symantec PcTelecommute File Syncronization
+POP2                   TCP      109            #Post Office Protocol Verion 2
+POP3                   TCP      110            #Post Office Protocol Verion 3
+667-Trojans            TCP      667            #Misc Trojans
+PPTP                   TCP     1723            #Point 2 Point Tunnelling Protocol
+Quake                  TCP     26000   UDP     26000           #Quake
+RainWall Command       TCP     6374            #RainWall High Avaiablility Deamon
+RAT                    TCP     1097-1098       #Remote Administration Tool Trojan
+####Real-Audio         TCP     7070            #Real Audio
+Remote-Strom           TCP     1025            #Used by Fraggle Rock, NetSPY and MD5 BackDoor Trojans
+####RTSP                       TCP      554            #RealTime Streaming Protocol
+Secure-ID-Prop         TCP     5510            #Token based Authentication Service
+ShadyShell             TCP     1337            #ShadyShell Trojan
+####RSH                        TCP      514            #Remote Shell
+SMTP                   TCP       25            #Simple Mail Tranfer Protocol
+Sockets-DES            TCP        1            #Also used by the TCP MUX Service
+SQLNET-1               TCP     1521            #Oracle SQL Net Verison 1 Service
+SQLNET-2-1521          TCP     1521            #Part of Oracle SQLNet Verison 2 Service
+SQLNET-2-1525          TCP     1525            #Part of Oracle SQLNet Verison 2 Service
+SQLNET-2-1526          TCP     1526            #Part of Oracle SQLNet Verison 2 Service
+SSH                    TCP       22            #Secure Shell
+####SSH2                       TCP       22            #Secure Shell Version 2 also blocks version 1
+Safe-T-Net             TCP     32557           #Safe-T-Net Configuration
+Squid                  TCP     3128            #Squid Proxy 
+StoneBeat-Control      TCP     3002            #StoneBeat Control
+StoneBeat-Deamon       TCP     3001            #StoneBeat Deamon HeartBeat
+SubSeven               TCP     27374           #Also used by BadBlood, EGO, Lion and WebHead Trojans
+T120                   TCP     1503            #H323 Application Sharing Protocol
+TACACS+                        TCP       49            #Terminal Access Control Access Control System
+Term-Serv              TCP     3389            #Terminal Server
+High-Ports             TCP     1024-65535      #TCP High Ports
+Telnet                 TCP       23            #Telnet Protocol
+TerrorTrojan           TCP     3456            #Terror Trojan
+TheFLU                 TCP     5534            #The FLU Trojan
+Time                   TCP       37            #Time Server Protocol
+TransScout             TCP     2004-2005       #TransScout Trojan
+Trinoo                 TCP     1524            #Trinoo Trojan
+UltorsTrojan           TCP     1234            #Also used by SubSeven Java Client
+UUCP                   TCP      540            #Unix 2 Unix Copy Program
+WAIS                   TCP      210            #Wide Area Information Servers
+####Winframe           TCP     1494            #Winframe
+X11                    TCP     6000-6063       #XWindows System
+Yahoo-Messenger                TCP     5050            #Yahoo Messenger
+Yahoo-Voice            TCP     5000-5001       #Yahoo Voice
+Yahoo-WebCam           TCP     5100            #Yahoo WebCam
+Webmin                 TCP     10000
+Usermin                        TCP     20000
+
+
+Archie         UDP     1525            #Archie Internet Protocol
+Biff           UDP      512            #Give notice of incoming mail messages
+Blubster       UDP     41170           #Uses Manolito Protocol P2P
+Bootp          UDP       67            #Bootstrap Protocol Server
+Citrix-ICABrowse UDP   1640            #Citrix ICA Browsing
+CU-SeeMe       UDP     7648-7652       #Video Conferencing
+DHCP-Reply     UDP       68            #DHCP Reply
+DHCP-Request   UDP       67            #DHCP Request
+DirectConnect  UDP     411-412         #DirectConnect P2p Application
+Discard                UDP        9            #Discard Server Protocol
+Echo           UDP        7            #Echo
+eDonkey-4665   UDP     4665            #eDonkey
+FreeTel-out    UDP     21300           #RealTime Full Duplex Voice Communication
+H323-RAS       UDP     1719            #RAS and Associated connection H323
+H323-RASOnly   UDP     1719            #Endpoint to Gatekeeper communications
+HackaTack-31789        UDP     31789           #HackaTack Trojan
+HackaTack-31791        UDP     31791           #HackaTack Trojan
+HotLine-Tracker        UDP     5499            #HotLine Tracker Connections
+ICQ-Locator    UDP     4000            #Mirabilis ICQ Version
+Interphone     UDP     22555           #VocalTec Internet Phone
+L2TP           UDP     1701            #Layer 2 Tunnelling Protocol
+Microsoft-DS   UDP      445            #CIFS over UDP
+MSN-1863       UDP     1863            #MSN
+MSN-1590       UDP     5190            #MSN
+MSN-Voice      UDP     6901            #MSN Voice
+MSSQL-MON      UDP     1434            #MSSQL Monitor
+MSSQL-Server   UDP     1433            #MSSQL Server
+Name           UDP       42            #HostName Server
+NBDatagram     UDP      138            #NetBIOS Datagram Service
+NBName         UDP      137            #NetBIOS Name Service
+RADIUS-2       UDP     1812            #Remote Authentication Dial-In User Service V2
+NoBackO                UDP     1201            #NoBackO Trojan
+OnTime         UDP     1622            #OnTime
+PcAnywhere     UDP     5632            #PcAnywhere
+RADIUS         UDP     1645            #Remote Authentication Dial-In User Service
+RainWall-Deamon        UDP     6372            #RainWall Deamon
+RailWall-Status        UDP     6374            #RainWall Remote Management Status
+RainWall-Stop  UDP     6373            #RainWall Monitoring
+RexxRave       UDP     1104            #RexxRave Trojan
+RIP            UDP      520            #Routing Information Protocol
+SecureID       UDP     5500            #Token Based Authentication
+SIP            UDP     5060            #Shared Whiteboard and Instant Messenger Apps
+SNMP           UDP      161            #Simple Network Management Protocol
+SNMP-Trap      UDP      162            #SNMP Trap
+SteamWorks     UDP     1558            #Steamworks
+SWTP-SMS       UDP     9282            #Software Management Server
+Syslog         UDP      514            #UNIX Syslog Protocol
+TACACS         UDP       49            #Terminal Access Control Access Control System
+TFTP           UDP       69            #Trivial File Transfer Protocol
+TIME           UDP       37            #Time Server Protocol
+High-Ports     UDP     1024-65535      #High Ports
+Vosaic-Data    UDP     20000-20030     #Vosaic Data
+Who            UDP      513            #UNIX Who Protocol
+WinMX          UDP     6257            #Also uses Napster Ports
+Yahoo V-Chat   UDP     5000-5010       #Yahoo Voice Chat
+
+Dest-Unreach   ICMP    3       #ICMP Destination Unreachable
+Echo-Reply     ICMP    0       #ICMP Echo-Reply
+Echo-Request   ICMP    8       #ICMP Echo-Request
+Info-Reply     ICMP    16      #ICMP Info Reply
+Info-Request   ICMP    15      #ICMP Info Request
+Mask-Reply     ICMP    18      #ICMP Mask Reply
+Mask-Request   ICMP    17      #ICMP Mask Request
+Param-Prblm    ICMP    12      #ICMP Parameter Problem
+Redirect       ICMP    5       #ICMP Route Redirect
+Source-Quench  ICMP    4       #ICMP Source Quench
+Time-exceeded  ICMP    11      #ICMP Time to Live Exceeded
+TimeStamp      ICMP    13      #ICMP Time Stamp
+TimeStamp-Reply        ICMP    14      #ICMP TimeStamp Reply   
+
+AH                     IP      51      #IPSec Authentication Header Protocol
+BackWeb                IP      17      #Push Web Application Directly to Desktops
+EGP                    IP       8      #Exterior Gateway Protocol
+ESP                    IP      50      #IPsec Encapsulation Security Paylod Protocol
+WEB_Mapped             IP       6      #HTTP and FTP Port Mapping Service
+IGMP                   IP       2      #Internet Group Management Protocol
+IGRP                   IP       9      #Cisco Interior Gateway Routing Protocol
+MSSQL_Resolver IP      17      #Block MSSQL Sapphire/Slammer Worms
+OSPF                   IP      89      #Open Shortest Path First
+RIP-Response   IP      17      #RIP Routing Response
+SMTP_Mapped            IP       6      #SMTP Port Mapping Service
+SSH_V2         IP       6      #SSH Version 2
+TraceRoute             IP      17      #Unix TraceRoute
+VRRP                   IP      112     #Virtual Router Redundancy Protocol
+GRE                    IP      47
diff --git a/itsecur-firewall/status_monitor.pl b/itsecur-firewall/status_monitor.pl
new file mode 100644 (file)
index 0000000..f4dfc16
--- /dev/null
@@ -0,0 +1,76 @@
+
+do 'itsecur-lib.pl';
+
+# status_monitor_list()
+# Just one type is supported
+sub status_monitor_list
+{
+return ( [ "rule", $text{'monitor_type'} ] );
+}
+
+# status_monitor_status(type, &monitor, from-ui)
+# Check the logs to see if the rule has been hit recently
+sub status_monitor_status
+{
+local $rv;
+if ($_[2]) {
+       # If this call is from the UI, then just return the current status
+       local %oldstatus;
+       &read_file("$config_directory/status/oldstatus", \%oldstatus);
+       $rv = { 'up' => defined($oldstatus{$_[1]->{'id'}}) ?
+                        $oldstatus{$_[1]->{'id'}} : -1 };
+       }
+else {
+       # Actually check the logs
+       local %lasttime;
+       &read_file("$module_config_directory/lasttime", \%lasttime);
+       local $l;
+       local $stime;
+       $rv = { 'up' => 1 };
+       foreach $l (reverse(&parse_all_logs(1))) {
+               if ($l->{'time'} > $lasttime{$_[1]->{'id'}}) {
+                       # Consider this line
+                       if ($l->{'rule'} == $_[1]->{'rule'}) {
+                               # Got a hit!
+                               $rv = { 'up' => 0 };
+                               }
+                       }
+               $stime = $l->{'time'};
+               }
+       $lasttime{$_[1]->{'id'}} = $stime || time();
+       &write_file("$module_config_directory/lasttime", \%lasttime);
+       }
+return $rv;
+}
+
+# status_monitor_dialog(type, &monitor)
+# Return form for selecting a rule
+sub status_monitor_dialog
+{
+local $rv;
+$rv = "<tr> <td><b>$text{'monitor_rule'}</b></td>\n";
+$rv .= "<td colspan=3><select name=rule>\n";
+local $r;
+foreach $r (&list_rules()) {
+       if ($r->{'log'}) {
+               $rv .= sprintf "<option value=%s %s>%s\n",
+                       $r->{'num'},
+                       $_[1]->{'rule'} == $r->{'num'} ? "selected" : "",
+                       &text('monitor_num', $r->{'num'},
+                               &group_name($r->{'source'}),
+                               &group_name($r->{'dest'}));
+               }
+       }
+$rv .= "</select></td> </tr>\n";
+return $rv;
+}
+
+# status_monitor_parse(type, &monitor, &in)
+# Parse form for selecting a rule
+sub status_monitor_parse
+{
+$_[1]->{'rule'} = $_[2]->{'rule'};
+}
+
+1;
+
diff --git a/itsecur-firewall/stop.pl b/itsecur-firewall/stop.pl
new file mode 100755 (executable)
index 0000000..e6bd10e
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/perl
+# stop.pl
+# Stop the firewall
+
+$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{'stop_doing'}\n";
+$err = &stop_rules();
+if ($err) {
+       print &text('stop_failed', $err),"\n";
+       exit(1);
+       }
+else {
+       print "$text{'stop_done'}\n";
+       &disable_routing();
+       exit(0);
+       }
+
diff --git a/itsecur-firewall/tail.cgi b/itsecur-firewall/tail.cgi
new file mode 100755 (executable)
index 0000000..c5bd5dc
--- /dev/null
@@ -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{'log'} || &get_log_file();
+print "Content-type: text/plain\n\n";
+
+# Get all the firewall log lines
+open(LOG, $log);
+while(<LOG>) {
+       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 = <LOG>;
+       print $line if ($line && &is_log_line($line));
+       }
+
diff --git a/itsecur-firewall/up.cgi b/itsecur-firewall/up.cgi
new file mode 100755 (executable)
index 0000000..d34e9a2
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/perl
+# up.cgi
+# Move a rule up
+
+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/welf.cgi b/itsecur-firewall/welf.cgi
new file mode 100755 (executable)
index 0000000..da5bd15
--- /dev/null
@@ -0,0 +1,154 @@
+#!/usr/bin/perl
+# Export all matching logs in WELF format
+
+require './itsecur-lib.pl';
+&can_edit_error("report");
+use POSIX;
+&ReadParse();
+
+@logs = &parse_all_logs();
+@logs = &filter_logs(\@logs, \%in, \@searchvars);
+if ($in{'save_name'}) {
+       push(@searchvars, "save_name=".&urlize($in{'save_name'}));
+       }
+
+# Build map of protos and ports to services
+@servs = &list_services();
+foreach $s (@servs) {
+       for($i=0; $i<@{$s->{'protos'}}; $i++) {
+               $proto = lc($s->{'protos'}->[$i]);
+               $port = $s->{'ports'}->[$i];
+               if ($port =~ /^(\d+)\-(\d+)$/) {
+                       foreach $p ($1 .. $2) {
+                               $multi_map{$proto,$p} = $s;
+                               }
+                       }
+               else {
+                       $serv_map{$proto,$port} = $s;
+                       }
+               }
+       }
+
+# Validate inputs
+&error_setup($text{'welf_err'});
+if ($in{'dest_mode'} == 1) {
+       $orig_dest = $in{'dest'};
+       if (-d $in{'dest'}) {
+               $in{'dest'} .= "/logs.welf";
+               }
+       $in{'dest'} =~ /^(.*)\// || &error($text{'backup_edest'});
+       -d $1 || &error($text{'backup_edestdir'});
+       $file = $in{'dest'};
+       $done = &text('welf_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('welf_done2', $in{'ftphost'}, $in{'ftpfile'});
+       }
+elsif ($in{'dest_mode'} == 3) {
+       $in{'email'} =~ /^\S+\@\S+$/ || &error($text{'backup_eemail'});
+       $file = "mailto:$in{'email'}";
+       $done = &text('welf_done3', $in{'email'});
+       }
+
+$temp = &tempname();
+open(OUT, ">$temp") || &error($!);
+$host = &get_system_hostname();
+foreach $l (reverse(@logs)) {
+       print OUT "id=firewall ";
+       @tm = localtime($l->{'time'});
+       print OUT "time=\"",strftime("%Y-%m-%d %H:%M:%S", @tm),"\" ";
+       print OUT "fw=$host ";
+       if (&deny_action($l)) {
+               print OUT "pri=4 ";
+               }
+       else {
+               print OUT "pri=5 ";
+               }
+       print OUT "rule=$l->{'rule'} ";
+       if ($l->{'proto'} && $l->{'dst_port'}) {
+               # Find the service name
+               local $serv = $serv_map{lc($l->{'proto'}),$l->{'dst_port'}} ||
+                             $multi_map{lc($l->{'proto'}),$l->{'dst_port'}};
+               if ($serv) {
+                       print OUT "proto=$serv->{'name'} ";
+                       }
+               }
+       print OUT "src=$l->{'src'} ";
+       print OUT "dst=$l->{'dst'}\n";
+       }
+close(OUT);
+
+# Send to destination
+($mode, @dest) = &parse_backup_dest($file);
+if ($mode == 1) {
+       # Move to destination
+       $out = `mv '$temp' '$file' 2>&1`;
+       &error($out) if ($?);
+       }
+elsif ($mode == 2) {
+       # FTP somewhere
+       local $err;
+       &ftp_upload($dest[2], $dest[3], $temp, \$err, undef, $dest[0], $dest[1]);
+       unlink($temp);
+       &error($err) if ($err);
+       }
+elsif ($mode == 3) {
+       # Email somewhere
+       $data = `cat $temp`;
+       unlink($temp);
+       $host = &get_system_hostname();
+       $body = "Firewall logs in WELF format from $host are attached to this email.\n";
+       local $mail = { 'headers' =>
+                       [ [ 'From', $config{'from'} || "webmin\@$host" ],
+                         [ 'To', $dest[0] ],
+                         [ 'Subject', "Firewall logs" ] ],
+                       'attach' =>
+                       [ { 'headers' => [ [ 'Content-type', 'text/plain' ] ],
+                           'data' => $body },
+                         { 'headers' => [ [ 'Content-type', 'text/plain' ] ],
+                           'data' => $data } ] };
+       $main::errors_must_die = 1;
+       if (&foreign_check("mailboxes")) {
+               &foreign_require("mailboxes", "mailboxes-lib.pl");
+               eval { &mailboxes::send_mail($mail); };
+               }
+       else {
+               &foreign_require("sendmail", "sendmail-lib.pl");
+               &foreign_require("sendmail", "boxes-lib.pl");
+               eval { &sendmail::send_mail($mail); };
+               }
+       return $@ if ($@);
+       }
+
+# Save settings
+$config{'welf_dest'} = $in{'dest_mode'} == 0 ? undef : $file;
+&write_file($module_config_file, \%config);
+
+if ($in{'dest_mode'} == 0) {
+       # Send to browser
+       print "Content-type: text/plain\n\n";
+       open(FILE, $temp);
+       while(<FILE>) {
+               print;
+               }
+       close(FILE);
+       unlink($temp);
+       &remote_webmin_log("backup");
+       }
+else {
+       # Tell the user
+       &header($text{'welf_title'}, "",
+               undef, undef, undef, undef, &apply_button());
+       print "<hr>\n";
+
+       print "<p>$done<p>\n";
+
+       print "<hr>\n";
+       &footer("/$module_name/list_report.cgi?".join("&", @searchvars),
+               $text{'report_return'});
+       }
+