sf Firewall User's Guide

sf Firewall Software--a free TCP/IP packet filtering firewall for Linux
Robert Muchsel and Roland Schmid

Version 0.3.0, last edited on February 20th, 1997

Welcome!

"Firewalls are the wrong approach. They don't solve the general problem, and they make it very difficult or impossible to do many things. On the other hand, if I were in charge of a corporate network, I'd never consider hooking into the Internet without one. And if I were looking for a likely financially successful security product to invest in, I'd pick firewalls."

Charlie Kaufman

This firewall software was written by Robert Muchsel and Roland Schmid, two former students from the Swiss Federal Institute of Technology Zurich, Switzerland, with input from Dr. Hannes Lubich. Dr. Lubich is a former employee of SWITCH, the Swiss Academic and Research Network. We also wish to thank Hans-Christian Boos of arago GmbH, Frankfurt, and René Fehr of Telekurs Logistik AG for their thorough testing.

This software is free.

Major new features in this release:

Will this software work for you?

If you have medium volume traffic to the Internet, the software is designed for you. A Linux PC must be able to route all your traffic, therefore you shouldn't use your trashiest 386 machine for this purpose (unless it is a 14.4K modem link, where even a 386 might suffice).

We have successfully tested the software

This software will not work if you are using the transparent IP proxying Linux feature.

Other sources

Apart from the documentation delivered with this package, we suggest you first print a copy of Chapman's article, since our firewall implements many of his concepts. If you are unfamiliar with firewalling and TCP/IP related threats, it might be wise to get Cheswick's and Bellovin's book also.

Some of the more advanced features of this software (remote configuration, firewall-to-firewall communication) require that you install the ENskip package also, version 0.65 or later.

Source code availability

We don't believe in security by obscurity. The source is yours to poke around, tailor it to your needs and eliminate all the bugs you can get hold of. We request, however, that you do not make publicly available modified versions of the source code. And it would be nice if you informed us of bugs and if you sent us any useful additions.

Features

You might have noticed the firewall is called "sf firewall" and not "yasbf" (yet another silly boring firewall). The sf firewall is an experimental firewall. In addition to a human-readable configuration language and a new graphical tool which generates configuration files for you, we implemented dynamic rules, variables and time-outs, extensive logging, alerting and counter intelligence, RIP, FTP (active and passive mode), ICMP, IGMP, UDP and TCP filtering and offer control of all IP fields. The firewall also prevents packet address spoofing and oversized IP packets. It optionally checks TCP sequence numbers, it can communicate with other firewalls and lets you kill idle or otherwise unwanted TCP connections.

Bugs?!

There's no such thing as bug-free software. However, we have enough faith in our software to let it protect a few networks in the industry.

Please don't try to hack our networks. Thank you.

Since we (the software authors) are not directly connected to the Internet, you will only harass some innocent people somewhere else.

How to contact us

Please address all correspondence to firewall-bugs@switch.ch. This is not a distribution list, so please do not send subscription requests.

We appreciate your feedback, your suggestions and bug reports and we'd like some example configurations for inclusion with the next version of the software.

Copyright

Copyright © 1996-1997 Robert Muchsel and Roland Schmid

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Bibliography

A few papers and books you should have read before trying to configure the sf firewall:

Essential
Recommended

---

Installation

This chapter describes how to install the sf firewall on a Linux 2.0.x system.

If you are planning to use remote configuration, the graphical user interface or the Firewall-to-Firewall protocol, please first install the ENskip secure IP layer package.

Then, unpack the archive in a directory, for example /usr/local/src. The files will be written into ./sf-0.3.0 (you have probably already done that).

We recommend using Linux kernel version 2.0.29, which is the latest version the firewall has been tested against (early 2.0.x kernels occasionally drop TCP connections; kernels 2.0.23 and earlier are vulnerable to oversized IP packets). There is a version of the sf firewall for kernels 1.2.x, please get the archive sf-0.1.tar.gz.

There is no need to patch the 2.0.29 kernel, however you should fix the bugs in your Linux distribution (e.g. sendmail). To minimize the impact of TCP SYN flooding attacks, it might be wise to install a patch.

If your current kernel configuration does not include "Loadable module support", "Networks firewall support" and "IP routing", you must recompile the kernel. To recompile the kernel, change to the /usr/src/linux directory (note: if your kernel tree is somewhere else, you must edit make.options in the sf directory). First run make menuconfig and select the appropriate options. Then compile by executing

make clean; make dep; make zImage.

For further information about compiling a Linux kernel, please refer to the Linux kernel HOWTO.

While the kernel is being compiled, you can continue with the firewall setup. Change to the sf-0.3.0 directory and type

./configure

View the file sf_custom.h and check if the information is appropriate for your system configuration. Do not change anything you are not sure about!

Now type

make clean; make dep; make

to generate the files sfc, sfident and sf.o.

Create a new user called firewall by adding the following line to your /etc/passwd file (you can use any unused user and group id):

firewall:x:888:888::/dev/null:/bin/false

Create a new group firewall by adding the following line to your /etc/group file:

firewall:x:888:

Now,

make install

This command creates the firewall device, the firewall pipes and copies the executable files to their final destinations.

Create the firewall configuration file /etc/firewall.conf. You should use an example file included in this package and change it to your needs or use the graphical user interface to create a customized version.

When the kernel compilation has succeeded, install the new kernel (copy zImage to /vmlinuz, run lilo and reboot).

Now you can insert the module (insmod sf) and start the firewall daemon (sfc start). For automatic startup at boot time, insert these two statements into a file in /sbin/init.d/ (or /etc/rc.d/, depending on your init style). Be sure to start the firewall daemon before the routing daemon. If your firewall configuration does not allow ICMP routing redirects, you should check your routing tables after the start of the firewall daemon for dynamically generated routes and delete them.

We recommend the following changes to your Linux system:

---

Firewall Configuration

The packet filter is set up by a configuration file specifying all rules and actions. This chapter describes all possible configuration options.

It is recommended that you read this chapter even if you use the graphical tool to create your configuration file, because there will still be some fine-tuning left to do.

For details concerning the syntax and the available options of the statements, please refer to the overview at the end of this chapter. It is recommended to use an example file provided with the firewall package or a file generated by the graphical tool as a starting point for your own configuration.

The configuration file is divided into three parts:

Any line in the configuration file starting with a #-sign is treated as a comment. C style comments are also allowed; they are normally used for comments covering multiple lines or not starting at the beginning of a line. (A C style comment is any text starting with /* and ending with */).

Note that you may only use lower case letters in the configuration file (strings can also contain upper case letters).

IP Addresses

IP addresses are used at several places in the configuration file. You can either use the dotted decimal notation or host names. In the latter case, the host name is translated into IP addresses when parsing the configuration file. All addresses returned by the resolver for the specified host name are inserted. It is strongly recommended not to use host names for the following reasons:

You can specify a mask after the address or host name. Note that this is normally not the netmask! Two addresses match if all bits match which are set to one in the mask. Thus, if you want the complete address to be equal, the mask is 255.255.255.255 (this mask is normally used to compare host addresses).

Examples:

129.132.1.18   mask 255.255.255.255   matches exactly one host
193.135.255.0  mask 255.255.255.0     matches all addresses in the class C net
129.132.20.0   mask 255.255.255.0     matches all addresses in the class B net
                                      129.132.20.0 using subnetting

You do not have to specify the mask if the address is either a host address or a net address without subnetting. The following examples show what mask is calculated automatically:

129.132.1.18   = 129.132.1.18  mask 255.255.255.255
129.132.0.0    = 129.132.0.0   mask 255.255.0.0
193.135.255.0  = 193.135.255.0 mask 255.255.255.0

Note that the address 129.132.20.0 without mask is treated as a host address, because the host part of the class B net is not zero! To specify a subnet address, you always have to specify the netmask as mask.

The Setup Section

The configuration file starts with the setup keyword.

The optional internalnets statement is used to inform the firewall about the IP addresses of the nets (and/or hosts) that are "inside" the firewall protected area. Normally these are the IP addresses allocated by your organization. The list of addresses is separated by commas and terminated by a semicolon. This information is used by the filter to prevent IP address spoofing: It determines if a packet arrived on the correct interface (a packet containing a source address out of the address space allocated by the organization must not arrive on the interface connected to the internet). It is assumed that there is only one connection to the internet within the organization, or that multiple connections are separated by routers.

You must specify a default mail address using the mail_default statement. This address is used if no other address is given in a mail statement or if a severe error occurs (e.g. no more log disk space). You may also specify a list of mail addresses separated by spaces within the string.

The trusted_hosts statement lists the hosts you will accept dynamic rules and log output from. This statement is used for the firewall-to-firewall protocol, which is covered in a chapter of its own. The friend_hosts statement list the hosts you accept log entries from.

The Configuration Section

The configuration section starts with the keyword "priority_rules" or "rules". Priority rules cannot be overridden by dynamic rules or rules received via the firewall-to-firewall protocol.

Each rule in the configuration section starts with one of the keywords accept, observe, block or reject:

Next, you may optionally provide a list of IP options using the keyword options. A packet matches the rule if at least one of the listed options is set. A special case is the "time to live" field, which is not really an option but is treated like one here. Its value can be compared with a constant, a packet matches the rule only if the comparison is true, regardless of other options listed.

Information about the protocol is provided with one of the keywords tcp, udp, icmp, igmp, rip or all, where all matches packets regardless of their protocol. Another possibility is to give the protocol number used in the IP header, for example 9 for IGP. Using icmp or igmp, the rule may be restricted to a list of message types.

rip is a special case of udp where all packets directed to port 520 are investigated. A RIP packet matches the rule if all announced destinations are listed. The following is an example for a RIP rule:

accept rip outside      /* matches all external addresses */
from 194.40.243.5       /* next router in the internet */
to 194.40.243.4;        /* own address */

A packet matches a rule if the source address is one of the addresses given after the from keyword and the destination address is one of the addresses given after the to keyword. If the from list or the to list is omitted, the packet matches regardless of the source and destination address respectively. Instead of the address list you may specify the inside keyword, which denotes all addresses provided in the internalnets statement (see above) or the outside keyword, which denotes all addresses not listed in the internalnets statement.

If the protocol of the rule is tcp or udp, you can also specify a port or a range of ports giving either the port number or the service name. The latter is translated to the number using the /etc/services file. Specifying a port with a protocol other than tcp or udp does not have an effect.

Here is an example for the from and to part of a rule:

from inside port telnet           /* all inside addresses with source port 23 */
to 129.132.0.0,                   /* all packets directed to this net */
port 2700,                        /* all packets directed to port 2700 */
130.103.24.0 mask 255.255.255.0
port 3000..4000;                  /* all packets to the specified subnet with
                                     3000 <= destination port <= 4000 */

At last you must specify the notification level, which tells the firewall what to do if the rule matches (apart from accept, observe, block or reject the packet). A level of zero means that the firewall daemon will not be informed about the event. If the level is greater than zero, the firewall daemon adds an entry to the firewall log file and performs all actions specified in the notification section (see below).

The order of the rules in the file is very important. If two rules overlap, the priority of the first one is higher than of the second one. The consequence of this is that you should start with specific rules and put general rules at the end of the section. For example, if you wanted to block all UDP packets except from the host 194.40.236.34, you would have to write two rules in the following order:

accept udp from 194.40.236.34 notification_level 0;
block  udp notification_level 1;

The Notification Section

The notification section starts with the notification keyword followed by a paragraph for each notification level. Each paragraph starts with the keyword level followed by the level number and a colon. You can specify a list of actions in each paragraph. The following actions are possible:

message
Defines additional text that is written to the log file and into mail messages. If the message covers multiple lines, you have to terminate the string with a double quote (") and restart it in the next line. Note that you must specify separate messages within if statements.
syslog
Logs the current log entry to the syslog file in addition to the entry into the firewall log file.
report
Copies the current log entry to the firewall.report log file in addition to the default log file.
mail
Sends an e-mail to the specified address(es). If no address is specified, the mail is sent to the address given in the mail_default statement.
spy
Starts counter intelligence, for example a back finger. You should use spy together with mail to receive the spy results as e-mail.
relevel
Changes the notification level for the rule that caused the action. The next time a packet matches that rule, the actions specified in the "new" level are executed.
exec
Executes an arbitrary shell command. For example, exec can be used to shut down an interface or the whole system, trigger the pager of the system administrator, etc. This is a very powerful command, use it with care!
call
Calls another notification level and executes it.
let
Assigns or changes the value of a variable. A time-out value (in seconds) can be specified at the end of the let statement. If a variable is updated or accessed within the specified time-out, the time-out is reset and the value is kept. Otherwise, the variable is reset to zero.
A filter rule
...can be specified that is added to the filter configuration dynamically. In addition to the options described above, there are some special features for dynamic rules. If you use the keyword currentprotocol instead of one of the protocol names, the protocol of the packet that triggered the action is used for the dynamic rule. In similar fashion, you can use the keywords sourcehost, sourcenet, desthost, destnet after to and from to insert the appropriate information into the dynamic rule. Finally, it is possible to specify a time-out value (in seconds) at the end of the rule. The rule will be deleted automatically after the specified time (in contrast to variables, the time-out will not be reset if a rule is triggered). Be aware that it can be dangerous to use dynamic rules, because you never know which rules are active at the moment. It is also difficult to determine how different dynamic rules can influence each other because the priorities of the rules depend on the order in which the events occur. To specify static rules which have priority over dynamic rules use priority rules.
if
You can use an if statement to specify a list of actions that are to be executed only if the given condition is true.

Variables are declared implicitly, the initial value is set to zero. They can hold only integer values. If you access or update variables, you can optionally specify one of the qualifiers sourcehost and desthost after the variable name, separated by a colon. A special instance of the variable is accessed or created using the source or destination address of the packet that triggered the action as identification. If a variable is updated using a qualifier, both the variable itself and the instance are updated. This feature can be used to count the number of occurrences of an event both in total and separately for each distinct source or destination address.

In the following example, the qualified variables and the time-out mechanism for variables and dynamic rules are used. If a host pings your net 100 times in sequence with a maximum time of 2 seconds between two pings, all packets from that host are blocked for 10 minutes. The notification level of the dynamic rule is set to zero to reduce the amount of data transferred from the filter to the firewall daemon.

accept icmp icmp_echo to inside notification_level 10;

notification

level 10:

  let pingcount:sourcehost := pingcount:sourcehost + 1 timeout 2;
  if pingcount:sourcehost > 100 then
    block all from sourcehost notification_level 0 timeout 600
  endif;

Writing Filter Rules

In this chapter we will discuss the topics that are specific to our packet filter. We strongly recommend to read the book "Firewalls and Internet Security" by Cheswick and Bellovin to get an idea about TCP/IP security problems and how to configure a packet filter appropriately.

Block or Reject Packets?

It is often difficult to decide whether to block or to reject packets. If you block a packet, it is treated by the sender as if it got lost during transmission. So normally a protocol or application inherent time-out mechanism will cause the packet to be retransmitted several times. If you send an ICMP error message back, the sender will immediately be informed that the packet cannot be delivered and the packet will normally not be retransmitted.

You should never reject packets if the protocol or the sending application ignores ICMP error messages (like for example some DNS implementations), because this would cause a flood of error messages to be generated. TCP does not ignore the ICMP error messages, but keeps retransmitting the packet anyway, because the unreachable error may be due to a temporary failure of an intermediate router. To reject TCP packets, a reset packet should be generated (reject with tcp_reset and reject with best both do this).

Many programs do not distinguish between the different ICMP unreachable messages, so if you send back a port unreachable message, it may have the same effect as a host unreachable message. That way a host may become completely unreachable which is in most cases not the intended effect.

You should not try to reject any ICMP error messages, as this would not work anyway due to the kernel implementation.

If you use the reject with best option, TCP packets are bounced with a reset; an ICMP port unreachable message is generated for UDP packets and all other packets are rejected with ICMP host unreachable.

When in doubt, block packets. This saves network bandwidth and eliminates the possibility that someone using a fake source address will use your host as a RESET or ICMP message generator. These messages will be delivered to the host really owning the source address and might cause trouble (for example, disrupt TCP connections). Rejecting packets is useful to speed up TCP connection requests, because often an identd query is sent by the target host:
reject with tcp_reset tcp to port auth ...

Filtering TCP Circuits

If you write rules for TCP, the from address specifies the originator of the connection. Return packets are automatically allowed for established connections. If the notification level is not zero, the logging information is passed to the firewall daemon only once at connection establishment time.

FTP

The FTP protocol requires a special treatment, because - in active mode - the data connection is initiated by the server. The default is to allow both active and passive mode transfers (modifier "ftp_all"). You don't have to allow the incoming data connection explicitly. Each control connection is filtered for PORT commands, which announce the establishment of a data connection to a specific port. For each PORT command found, the specified data connection is allowed automatically (a similar technique is used for passive mode).

The modifier "ftp_none" disables the creation of dynamic rules for data connections of both active and passive mode data transfers. This means that a data connection will not be immediately accepted, but it will be treated like any other connection (given the ports used by data connections, the connection will most likely be blocked by your rules).

"ftp_active" scans for PORT commands (active mode transfers) and "ftp_passive" scans for "227 replies" (passive mode transfers). Both modifiers insert dynamic accept rules for data connections.

The "ftp_log_data_conn" modifier logs all ftp data connections (the default is to only log the control connections).

These modifiers can be placed after the "all" or "tcp" protocol identificators, e.g.
accept tcp ftp_passive, ftp_log_data_conn to port ftp from...

Sequence numbers

The sequence numbers of TCP circuits are checked if you specify the "check_sequence" modifier. Sequence number checking has a performance impact, it is however useful if you are using Berkeley derived TCP/IP implementations which have insecure sequence numbers. These TCP/IP stacks are vulnerable to sequence number guessing attacks.

Example:
accept tcp check_sequence to port telnet from...

Inbound and Outbound Packets

The packet filter is called each time an IP packet is sent or received. A forwarded packet is examined only at receive time, at send time only the interface is checked (for address spoofing). Local packets (i.e. packets that are not going to leave the host) are examined only at send time.

Address Spoofing

An address spoofing alarm is set off in one of the following cases:

  1. The source address is "inside" and the receiving interface is "outside" (or vice versa). This means that some exterior host may be trying to gain access to an interior host.
  2. The destination address and the sending interface do not match. This means that the routing table is probably corrupt, because it is sending the packets via the wrong interface.
  3. The loopback address is used on any interface other than the loopback interface.

Note that class D (multicasting) and class E addresses are not checked against address spoofing.

Fragmented IP Packets

Only the first fragment of an IP packet is examined and the result of the examination is stored. All subsequent packets are silently discarded if the first fragment was not accepted. If the fragments do not arrive in order, all fragments that arrive before the first one are blocked.

It is suggested to use the built-in Linux packet defragmentation feature instead, which automatically disables the firewall fragment checking code.

Syntax of the Configuration File

configuration     = setup_section
                    [ priority_section ]
                    rules_section
                    notification_section
                    "end.".
setup_section     = "setup"
                    [ setup_statement ]
                    mail_statement
                    [ trusted_statement ]
                    [ friends_statement ].
priority_section  = "priority_rules" { (  block_statement
                               | accept_statement
                               | reject_statement
                               | observe_statement ) }.
rules_section     = "rules" { (  block_statement
                               | accept_statement
                               | reject_statement
                               | observe_statement ) }.
setup_statement   = "internalnets" address { "," address } ";".
trusted_statement = "trusted_hosts" address { "," address } ";".
friends_statement = "friends_hosts" address { "," address } ";".
mail_statement    = "mail_default" """ mailaddress(es) """ ";".
block_statement   = "block" rule ";".
accept_statement  = "accept" rule ";".
observe_statement = "observe" rule ";".
reject_statement  = "reject" [ "with" (  "icmp_net_unreachable"
                                       | "icmp_host_unreachable"
                                       | "icmp_protocol_unreachable"
                                       | "icmp_port_unreachable"
                                       | "tcp_reset"
                                       | "best"
                                       | "echo_reply" ) ]
                     rule ";".
rule = [ "options" ip_option { "," ip_option } ]
     (  "all"  [ modifier  { "," modifier  } ]
      | protocol_number
      | "icmp" [ icmp_type { "," icmp_type } ]
      | "igmp" [ igmp_type { "," igmp_type } ]
      | "tcp"  [ modifier  { "," modifier  } ]
      | "udp"
      | "rip" [ ( address { "," address } | inside | outside ) ] )
     [ "from" ( fulladdr { "," fulladdr }
               | "inside" [ "port" port [ ".." port ] ]
               | "outside" [ "port" port [ ".." port ] ] ) ]
     [ "to"   ( fulladdr { "," fulladdr }
               | "inside" [ "port" port [ ".." port ] ]
               | "outside" [ "port" port [ ".." port ] ] ) ]
     "notification_level" value.
ip_option = (  "record_route"
             | "timestamp"
             | "security"
             | "loose_source_route"
             | "strict_source_route"
             | "sat_id"
             | "time_to_live" ( "<" | "=" | ">" | "!=" ) value ).
modifier  = (  "ftp_all"
             | "ftp_active"
             | "ftp_passive"
             | "ftp_none"
             | "ftp_log_data_conn"
             | "check_sequence" ).
icmp_type = (  "icmp_echo_reply"
             | "icmp_destination_unreachable"
             | "icmp_source_quench"
             | "icmp_redirect"
             | "icmp_echo_request"
             | "icmp_time_exceeded"
             | "icmp_parameter_problem"
             | "icmp_timestamp"
             | "icmp_timestamp_reply"
             | "icmp_info_request"
             | "icmp_info_reply"
             | "icmp_address"
             | "icmp_address_reply" ).
igmp_type = (  "igmp_host_membership_query"
             | "igmp_host_membership_report"
             | "igmp_host_leave_message" ).
fulladdr  = (  address [ "port" port [ ".." port ] ]
             | "port" port [ ".." port ] ).
address   = ( ip_address | """ name """ ) [ "mask" mask ].
port      = ( port_no | name ).
notification_section = "notification"
                "level" ( value
                         | "spoof"
                         | "oversized"
                         | "privileged_ftp"
                        ) ":" ( { entry ";" } | ";" )
              { "level" ( value
                         | "spoof"
                         | "oversized"
                         | "privileged_ftp"
                        ) ":" ( { entry ";" } | ";" ) }.
entry     = (  "message" """ text """ { """ text """ }
             | "syslog"
             | "report"
             | "mail" [ """ mailaddress(es) """ ]

             | "spy"
             | "exec" """ command """
             | "relevel" value
             | "call" value
             | "if" ( variable | value ) ( "<" | "=" | ">" | "!=" )
                    ( variable | value ) "then"
                { entry ";" } "endif"
             | "let" variable ":="
               ( value | "destport" | variable
                                      [ ( "+" | "-" ) ( value | variable ) ] )
                [ "timeout" seconds ]
             | dynamic_rule [ "timeout" seconds ]
            ).
variable  = name [ ":" qualifier ].
qualifier = ( "sourcehost" | "desthost" ).
[ special keywords allowed in dynamic_rule:
  "currentprotocol"  uses protocol of actual packet
  "sourcehost"       uses source host address
  "sourcenet"        uses source net address
  "desthost"         uses destination host address
  "destnet"          uses destination net address     ]

---

Examples

First, please carefully read the syntax description. In this section we'll describe a working (though not very useful) example making use of "notifications". For your convenience, this example is included with the distribution in source form (including some statements left out below for brevity).

In the following example, the text is printed in a monospaced font. Important keywords are printed in bold, and comments are italic.

# Sample configuration for the sf firewall

setup

internalnets 193.194.195.0;
mail_default "adm@x.y.z";

rules

# Some simple alarming rules to detect hackers

block tcp to port 87, /* link */
             port 95  /* supdup */
             notification_level 99;

block options loose_source_route, strict_source_route all
             notification_level 13;

# ...omit some more RIP statements

# detect secondary routes to the internet

block rip from inside notification_level 12;

# ...omit some more statements having notification level 0

# Permit incoming FTP requests to our FTP server only

accept tcp to 130.59.4.16 port 21 notification_level 1;

# Permit incoming Telnet requests to our Telnet/Login Server only

accept tcp to 130.59.4.16 port 23 notification_level 2;
accept all from 127.0.0.1 to 127.0.0.1 notification_level 0;

# block all other TCP connection requests and trigger the alarm

block tcp notification_level 99;

notification

level 1: /* log all permitted incoming FTP connection requests */
  message "FTP connection request.";

level 2: /* log all permitted incoming Telnet connection requests */
  message "Telnet connection request.";

level 3: /* log all permitted incoming WWW connection requests */
  message "WWW connection request.";

level 11:
  message "ICMP redirect received.";

level 12:
  message "There may be a secondary route to the internet.";

level 13:
  message "IP packet with source route option detected";
  let sr:sourcehost := sr:sourcehost + 1 timeout 300;
  if sr:sourcehost = 1 then
    message "IP packet with source route option detected";
    spy;
  endif;

level 99:
  message "Illegal TCP connection.";
  syslog;
  let illtcp:sourcehost := illtcp:sourcehost + 1 timeout 600;
  if illtcp:sourcehost = 1 then
    message "Illegal TCP connection.";
    mail;

    spy;
  endif;

level 999: /* This is never called but it shows exec */
  exec "ifconfig eth0 down"; /* bye-bye */

end.

The above example shows the use of statements in the "notification" section, but no dynamic rules and no relevel.

Releveling a rule is permanent--in contrast to temporary dynamic rules, which override static rules. In general, releveling is not a very good idea and should be substituted by temporary dynamic rules.

Suppose level 100 was the notification level triggered when the host is pinged. The log file would grow too fast if we logged every single ping.

The first idea is to relevel the rule:

...
notification

level 100:
  message "We have been pinged";
  relevel 0;
...

However, here a single ping suffices to eternally disable the "ping detection". To detect the first ping and be silent thereafter, we'd rather avoid relevel and use:

...
notification

level 100:
  let pings:sourcehost := pings:sourcehost + 1 timeout 600; /* 10 minutes */
  if pings:sourcehost = 1 then
    message "We have been pinged";
  endif;
...

If we are concerned of a denial-of-service attack, we might want to use a dynamic rule and block subsequent pings (after a certain threshold):

  if pings:sourcehost > 1000 then
    message "Possible denial-of-service attack";
    spy;
    block all from sourcehost notification_level 0 timeout 600;
  endif;

Of course, a real denial-of-service attack would probably make use of excessive ftp connects, not pings.

---

Starting and Controlling the Firewall

Once you have installed and configured the firewall, you might wonder how to get it up and running, how to stop it and how to display the current configuration. Well, you might even read this before installing the software. Clever!

This section describes the sfc program, which provides a command line interface to the firewall daemon and the kernel filter module.

Starting the Firewall

First, ensure that the kernel filter module sf.o has been inserted (the lsmod command will help you). If sf.o is not inserted, please consult the Installation Guide--and add the line insmod /the/path/to/sf.o to the startup file of your choice and reboot.

The sfc start command starts the firewall daemon and a stub process, which is used to automatically restart the daemon when the remote configuration tool has changed the configuration or when the daemon has crashed.

The default configuration file is /etc/firewall.d/firewall.conf, but you can specify any other file as a third parameter to sfc, e.g. sfc start myconfig.

The start command first parses the configuration file. It then checks that the daemon is not running, opens the log files, forks a daemon process (the stub), forks again (the daemon) and gives up its root privileges.

Stopping the Firewall

To stop the daemon and the stub process, invoke sfc stop, which signals the firewall daemon to exit.

Warning: When the firewall daemon has exited, all network traffic will be blocked. You must remove the kernel filter module sf.o to restore network functionality (rmmod sf.o). Alternatively, you can start a new daemon process (sfc start).

Reason: Suppose there was a bug in the operating system and some evil hacker managed to send a SIGKILL signal to the firewall daemon and the stub. The daemon would exit and leave the system unprotected. Indeed, there was a bug in the Linux 1.2.11 kernel which would allow any user to kill any process. This is one more reason to never allow users on the firewall machine.

Reconfiguring the Firewall

In some cases, it might be desired to automatically switch between different firewall configurations. Imagine an organization where nobody works on Sundays (I've been told such organizations exist!?). Therefore, network traffic should be monitored much more strictly on Sundays than on normal work days.

If you want to switch configurations, you could tell cron to do a sfc stop; sfc start newconfig. However, there's a better way built into sfc: sfc reconfig newconfig (the newconfig parameter is optional and will be substituted by /etc/firewall.d/firewall.conf if you omit it).

The reconfig command ensures seamless switching of configurations, i.e. existing TCP connections are allowed to continue. To modify this behavior, there are two optional parameters:

flush: The "flush" parameter ends all existing TCP connections which are not allowed under the new configuration. Other TCP connections are allowed to continue.

flush_all: "flush_all" ends all TCP connections (essentially the same as the stop; start sequence).

Checking a Configuration File

To check a configuration file for syntactic errors, you can run sfc check myconfig (once again, myconfig is optional and will be substituted by /etc/firewall.d/firewall.conf if omitted).

In case of an error, the line number will be displayed. Please check the syntax description for more information.

Displaying the Current Configuration

sfc show shows all active rules and variables and all active TCP connections (see below).

Active rules:
The active rules are fetched from the kernel filter module and displayed in the same order as checked by the filter:
Active rules -- usage counters last reset at Mon Feb 10 16:32:43 1997
------------

  1 (line  27) dynamic, timeout Aug 03 15:15:03
    block, notification level 0
    from 193.194.195.196 mask 255.255.255.255

  2 (line  10) static -- usage: 6 times, 1464 bytes
    block, notification level 7
    protocol RIP
In the first column, the sequential number of the rule will be displayed, in the same order as checked by the kernel filter module. Next, the line number in the configuration file and the type of the rule are given. If the rule is a dynamic rule, the time-out is also shown.
The second line shows what the filter will do if the rule is triggered (block or accept a packet, etc.) and the corresponding notification level.
All following lines are optional. Where applicable, the protocol type and packet origin or destination will be displayed.
In addition, the software also shows how often a (static) rule has been triggered, how many bytes were transferred (including ftp data connections, if applicable) and when the usage counters were last reset. This information can be used to implement accounting.
To reset the usage counters manually, use sfc resetusage. The counters are also reset if you reconfigure, start or stop the daemon.
Variables:
The variables are read from the firewall daemon.
  Variables:
  ----------

  mails = 0

  pings = 5, timeout Aug 03 15:13:53
      host 193.194.195.196 = 3, timeout Aug 03 15:13:53

  alerts = 3

The output shows the name of the variable and its total value. If the variable is to forget its value (i.e. it is a dynamic variable), the time-out is also displayed.

If the variable is subdivided into different hosts, each host is listed with IP address, value and time-out (where applicable).

TCP connections

The firewall software manages a table of all TCP connections. This table can be displayed by sfc show (or, omitting the rules and variables, sfc showtcp).

TCP connections at 16:29:19:
----------------------------
id/key   created  last use timeout  state/hosts client          server
-------- -------- -------- -------- ----------- sequence window sequence window
00000001 021beead 021bf5f7 00000000 ESTABLISHED 00000000 000000 00000000 000000
    01ec 16:22:44 16:23:02 -none-   130.60.105.3:1023->129.132.179.13:ssh
00000002 021c6454 021c88aa 021cb78a CLIENT_SYN  00000000 000000 00000000 000000
    014c 16:27:45 16:29:18 119 sec  130.60.105.3:1198->130.60.105.1:7227
00000003 021c7267 021c8993 00000000 ESTABLISHED 00000000 000000 00000000 000000
    0161 16:28:21 16:29:20 -none-   129.132.179.92:1023->130.60.48.131:ssh
00000004 021c88e5 021c88f7 00000000 ESTABLISHED 00000000 000000 00000000 000000
    00c1 16:29:18 16:29:19 -none-   130.60.105.3:1022->130.60.48.131:ssh

This connection listing shows an id/key for every connection, creation and last usage times (both in hex and human readable), a timeout (if any, both hex and human readable), the state of the connection and the IP/port addresses of server and client.

Note that two lines are displayed for every connection.

If checks for sequence numbers are done, the sequence numbers and the sequence number window sizes are also valid. For more information about TCP states and sequence numbers, please refer to the implementation guide.

Killing TCP connections

If there are active TCP connections you dislike for any reason, you can kill them using the sfc kill command. To prevent the accidental killing of connections, sfc kill takes three arguments from the TCP connection listing, namely id, key and created. These fields are marked red (id), blue (key), green (created) for the first connection in the sample listing above.

To kill all TCP connections, use sfc killall.

Killing idle TCP connections

While manually killing connections might be fun for some people (it is!), there's a more useful command: sfc killidle. sfc killidle kills connections based on the last usage field. This command is intended to be used automatically in a cron job.

Example:
sfc killidle 60
kills all connections which have been idle for at least 60 minutes.

---

Counter Intelligence

Some words about the counter intelligence built into the sf firewall daemon:

Some people consider "spying" on other hosts unethical, even if a host might be the source of an attack. To pacify those people, the sf firewall will only use counter intelligence if told so by the spy keyword. Thus the decision is left to you.

The intention of "spying" on foreign hosts is to learn both the foreign host's name and the name of the user on the foreign host who is responsible for triggering a firewall notification.

Actions of the spy Process

Please refer to the implementation guide for a description of the precautions the "spy" process takes when using the protocols presented below.

To learn the host name, the DNS (domain name system) is queried. The result is then reverse translated into an IP address and compared with the original address. If the IP address doesn't match, a special warning is generated--someone might have tampered with the DNS.

The next step, to find out the user name, is much harder. There is no standard way of doing so, and in fact many hosts reject all attempts of the firewall daemon aimed at learning a user name. Worse yet, some hosts even return outright lies. Please keep this in mind when reading the "spy" output.

The first attempt is the ident protocol. This protocol can only be used if the action which triggered the firewall notification is a TCP connection to the firewall host (this is a restriction of the protocol). If everything works as intended, you'll get the user name. If not, you'll either see a "no such user" error message or "connection refused".

The second try is to finger the foreign host. If the host allows finger requests, a list of all users currently logged into the system will be returned. This list contains all sorts of more or less interesting information like login names, real names, online times, and the users' .plan files (control sequences are removed from the output). Some hosts only return a short message equivalent to "To send mail to someone at ..., address your mail using the following format: ...". Other hosts refuse finger requests altogether.

The last try is the rusers command. I have yet to see a host where rusers requests are served, but if it works you'll get a list of user names, the host the user logged in from and online and idle times.

ident, finger and rusers will only work if you enable the respective ports for the firewall host in the firewall configuration file. rusers also needs the portmapper.

Output

If you don't do anything special, the output is appended to the firewall.spy file in the log file directory. If you put the mail keyword in the same notification section as the spy keyword, the results will also be e-mailed.

Example Output

The output might look similar to the following example (in real life, there will be more than two users logged in, and of course the example is 100% fictitious):

FIREWALL COUNTER INTELLIGENCE REPORT, Aug 09 12:20:02

Triggered by: (s 13) accept TCP 1.2.3.4:1065->193.194.195.196:telnet

  Host address: 1.2.3.4
  Host name:    oval.office.gov

identd information:
  User ID: unabom.

finger information:
  [1.2.3.4]
  Login: unabom                   Name: John Fitzgerald Doe
  Directory: /home/unabom         Shell: /bin/csh
  On since Wed Aug  9 10:27 (EST) on tty1
  No Mail.
  No Plan.

  Login: bclinton                 Name: Bill Clinton
  Directory: /home/bclinton       Shell: /bin/bash
  On since Wed Aug  9  9:32 (EST) on tty2, idle 0:22
  New mail received Wed Aug  9 12:18 1995 (EST)
       Unread since Tue Aug  8 20:03 1995 (EST)
  Plan:
    We are using PGP to encrypt our E-mail here at the office. Ha!

rusers information:
  unabom     localhost:tty1   Aug  9 10:27
  bclinton   localhost:tty2   Aug  9  9:32    :22

---

Log Output

Depending on your configuration, the firewall daemon generates little to huge amounts of log information (you'll receive an e-mail if the disk space gets low on the log device). This section is intended to help you understanding the output.

Output Destination

All output is written to the firewall log file. If you specify the syslog keyword, an entry will be made in the system log file, too. The report keyword copies the entry to the firewall.report file (intended for a daily summary). And if you use the mail keyword, the log entry will be e-mailed (this might be useful in case someone breaks into the system--no matter what the hacker tries, he will not be able to delete the e-mail which could have traveled half way around the globe already).

Abbreviations

Because of the abbreviations, the log output looks a bit cryptic for the beginner. This is a trade-off; we are trying to provide as much information as possible on one line (the line will be wrapped automatically if needed).

Feb 10 16:28:20 (s 105) accept TCP 129.132.179.92:1023->130.60.48.131:ssh
       |          |       |     |         |
       |          |       |     |         Packet source and destination
       |          |       |     Protocol
       |          |       Filter action
       |          Rule type and configuration file line number
       Date and time

The following is a listing of possible abbreviations (for details of the configuration, please refer to the complete description):

The other log entries should be self-explanatory.

---

Concepts

This chapter explains some of the concepts and terms referred to in other parts of this manual. However, this short overview is not intended to replace the implementation guides and the basic concept papers (which are available only in German and/or PostScript format, I'm afraid).

Firewall-to-Firewall Protocol

In its present implementation, the firewall-to-firewall protocol can be used to exchange dynamic rules and log messages between firewalls. ENskip is required to ensure authenticated communication.

The firewall-to-firewall protocol can be used to ensure a common set of rules on all firewalls of an organization and to assist system operators in data collection.

Firewall classes - Friends

In the notion presented here, two firewalls are "friends" if they exchange log information. Friends are specified using the "friends_hosts" keyword. A friendship relation may be unidirectional like in real life--i.e. you will send your log entries to all friends and accept log entries from all friends, but your friends may still decide to safely store your messages in /dev/null.

You must add accept rules for UDP port 7228 (both source and destination) for all of your friends, but block it for all other hosts.

Firewall classes - Trusted

A firewall trusts another firewall if it also accepts block, reject and observe rules created by another firewall. For security reasons, the trustee will ignore accept rules (dynamic accept rules are not a good idea anyway, because they tend to obscurely introduce holes in the security concept).

Typically, trusted firewalls are under common administrative control. It should be noted that the firewalls need a similar configuration for the rule exchange to work. "Similar" configuration means that the notification level numbers of dynamic rules should exist on both hosts (if a host receives a nonexisting notification level, the default level - generate a log entry - is used).

Trusted hosts are specified by the "trusted_hosts" keyword. This relation may be unidirectional, too.

You must add accept rules for UDP ports 7227 and 7228 (both source and destination) for all trusted hosts, but block it for all other hosts.

ENskip support

The firewall software not only uses the ENskip secure IP layer for firewall-to-firewall communication, but both packages are designed to operate seamlessly together.

In general, a packet first passes the ENskip layer, then the incoming firewall, gets forwarded by the kernel, goes through the outgoing firewall checks and reaches the ENskip layer again. This means that the firewall software will never see any SKIP packets (IP protocol 57, by the way), but only the decrypted and authenticated payload of these packets. Most commonly, this payload will be TCP or UDP which the firewall can understand. However, this also means that there will not be any log entries for authentication errors in the firewall log, and that you cannot use dynamic rules or the firewall scripting language for SKIP packets (only for the payload).

SKIP Certificate Discovery Protocol

To allow the SKIP certificate discovery protocol to exchange public keys, you must add accept rules for UDP ports 1639 and 1640 (cert-initiator, cert-responder, both directions).

ENskip firewalling

ENskip can be used to force the use of authentication and decryption from and to certain hosts. This can be viewed as a form of firewalling, because it denies unauthorized access. In fact, it is the only form of access control I'd really recommend.

Please refer to the ENskip documentation for more information (hint: "output filter after = IP", "input deskip policy = AUTH CRYPT").

---

Limits

This section deals with the limits of our firewall system.

Present Situation

Presently, most systems are completely unprotected. Many organizations don't even bother to replace buggy client software. For example, stone aged sendmails are still working on some neglected print servers in the basement, which are often retired work stations not powerful enough for modern software (in this context, a stone aged sendmail is any sendmail older than about 80 days, because that's the average schedule new security flaws are discovered).

Any firewall software, be it buggy or unsafe itself, will therefore improve the situation.

IPv6 will not really change anything. Although authentication and encryption are defined in the standard, it's not unlikely only a selected few will actually implement and use the authentication and encryption headers (these headers can also be used with IPv4, are used with the SKIP protocol, and are you using SKIP?). At last, an attacker will have a perfectly secure connection to an insecure server--neither weak server programs nor administration errors will be eliminated by IPv6. For more information, read the DFN-CERT paper by Uwe Ellermann about IPv6 and firewalls.

Protection by the sf Firewall System

We'd really like to tell you that you don't have to worry any more. But that's untrue. The sf firewall system will prevent some (many? most?) attacks, but not all attacks. Similar to a locked door, the software puts obstacles into the way of the evil. But if the wicked man keeps trying,
he might succeed in smashing the window or breaking open the door (ancient proverb).

In doing so, the attacker will most probably make tremendous noise, which our software will aid you detecting. Thus, your chances are increasing of being smarter and faster than he is (he might not have the sf firewall, so he might be faster but not really smarter--if you catch him, offer him a copy of the software).

Keep in mind the software is experimental and we are making no guarantees (except one: there are bugs).

Remaining Loopholes

A packet filter, even if beefed up like ours, cannot protect you against the hijacking of TCP sessions, modifying, intercepting and monitoring packet data somewhere along the route.

You will not be protected against password monitoring (even if you scramble your passwords, the packets can be replayed). For this reason, you should install one time passwords.

The system provides no means to aid you in authentication. To really know who the "other side" is, you will need dedicated software. Get ENskip now and start dancing.

------------------

Copyright © 1996-1997 Robert Muchsel and Roland Schmid.