sf Firewall Software--a TCP/IP packet filtering
firewall for Linux
Robert Muchsel and Roland Schmid
Version 0.3.0, last edited on February 20th, 1997
"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."
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.
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.
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.
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.
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.
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.
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 © 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.
A few papers and books you should have read before trying to configure the sf firewall:
Chapman, D.B. 1992:
Network (In)Security Through IP Packet Filtering.
Great Circle Associates, Mountain View, California.
Available
for ftp from ftp.greatcircle.com.
Bellovin, S.M. 1989:
Security Problems in the TCP/IP Protocol Suite.
AT&T Bell Laboratories, Murray Hill, New Jersey.
Available
for ftp from ftp.research.att.com.
Cheswick, W.R. and Bellovin, S.M. 1994:
Firewalls and Internet Security--Repelling the Wily Hacker.
Addison-Wesley, Reading, Massachusetts.
Kaufman, C., Perlman, R., Speciner, M. 1995:
Network Security--PRIVATE Communication in a PUBLIC World.
Prentice Hall, Eaglewood Cliffs, New Jersey.
Stevens, W.R. 1994:
TCP/IP Illustrated, Volume 1--The Protocols.
Addison-Wesley, Reading, Massachusetts.
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:
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 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 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 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 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:
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;
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.
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 ...
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.
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...
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...
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.
An address spoofing alarm is set off in one of the following cases:
Note that class D (multicasting) and class E addresses are not checked against address spoofing.
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.
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 ]
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.
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.
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.
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 .
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).
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.
sfc show shows all active rules and variables and all active TCP connections (see below).
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
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).
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.
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.
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.
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.
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.
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.
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
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.
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).
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):
Rule Types:
s = static rule
d = dynamic rule
SPOOF = implicit spoof rule (includes interface name)
XSIZE = implicit oversized packets rule (includes interface name)
FTP = implicit rule called if ftp data connection logging is enabled
ACTIVE FTP = implicit rule called if an attempt is made to open
an active FTP data connection to a privileged port
Filter Actions:
accept
block
observe
reject (includes reject type)
Protocols (excerpt):
RIP
TCP
UDP
ICMP
The packet source and destination will include ports if the protocol supports them.
The other log entries should be self-explanatory.
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).
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.
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.
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.
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).
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 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").
This section deals with the limits of our firewall system.
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.
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).
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.