TCP_wrappers provide a simple, elegant and effective means to safeguard your network services.
With the Internet growing by leaps and bounds, the security of computer systems has become a major focus of large corporations, small businesses and individuals alike. Hardly a week goes by without a security flaw being discovered in some network. Many companies are reducing the threat by installing firewalls between their internal networks and the Internet, but this option is generally too expensive and cumbersome for single users running Linux from home or office. TCP_wrappers was written by Wietse Venema, Eindhoven University of Technology, The Netherlands. It provides a simple, elegant and effective means to safeguard your network services from being accessed and possibly abused by intruders.
In this article we will discuss what TCP_wrappers are and how they work, and how to configure TCP_wrappers to protect your machine from unauthorized access. We'll also discuss some of the more advanced features of TCP_wrappers which provide detailed logging and can even help track attempts to break into your machine.
First, we need to know how a transmission control protocol (TCP) connection, such as telnet, is accomplished. TCP network connections are based on the “client/server” model. The telnet program is a client that communicates with a server program, or daemon, called telnetd or in.telnetd, depending on how your machine is set up. Since most Linux distributions use the name in.serviced in the directory /usr/sbin for network daemons, I will use that naming convention for the remainder of this article.
All requests for network services first go through the “Internet daemon”, inetd. (As with all things in life, there are exceptions to this rule, see “What TCP_wrappers Cannot Do” below.) This daemon uses two configuration files to determine how to respond to requests for network connections. The file /etc/services lists the names of particular services and the port numbers for those services. The file /etc/inetd.conf lists the names of the services and the names of the programs or daemons providing the services. Listing 1 and Listing 2 contain some sample lines from the /etc/services and /etc/inetd.conf files.
If I sit down at my.linux-box.com and type the command:
My telnet client sends a packet of information containing (among other things) the Internet address of the source my.linux-box.com, the Internet address of the destination your.machine.com and the port number for the connection. The port number for telnet is 23. inetd looks up port 23 in /etc/services and finds the service name telnet. It then looks up telnet in /etc/inetd.conf and finds that it needs to run the daemon called in.telnetd, listed in the rightmost column of Listing 2. inetd runs in.telnetd connecting it to port 23 and then goes about the business of listening for more connections. in.telnetd responds to my client, asking for a user name and password and starting up the telnet session.
What if you don't want anyone else to telnet into your computer? You can modify the code for in.telnetd to look at the source address of the connection request and to reject any addresses from outside your local machine or domain. If telnet were the only network service this would be easy, but there are dozens of network services, and it would be a nightmare to modify every daemon to limit access to your machine.
At this point TCP_wrappers comes to the rescue. The wrapper program is a tiny daemon that stands between inetd and network daemons such as in.telnetd and in.ftpd. Since all TCP connections are started up in basically the same fashion, a single wrapper program can be used to control access to almost all TCP network services.
When wrappers are installed, the Internet daemon is reconfigured to run the wrapper instead of the ordinary network daemon. The wrapper checks the source address of the connection and the service it wishes to connect to and decides whether to allow the connection. If your.machine.com denies my request for a telnet session, all I see is a dropped connection. If the request is allowed, everything proceeds normally, and the wrapper never actually interacts with my telnet client. In either case, the wrappers write a note in the system logs to let you know whether I was successful in connecting to your machine.
Every major Linux distribution comes with TCP_wrappers installed as part of the networking package. To see if you have TCP_wrappers installed on your machine, look in the /etc directory for two files called hosts.allow and hosts.deny. These are the configuration files used by the TCP_wrapper daemon, tcpd. You can also look at your /etc/inetd.conf file for lines like this:
telnet stream tcp nowait root /usr/sbin/tcpd \ in.telnetd
The telnet option /usr/sbin/tcpd indicates that whenever someone tries to telnet to your machine, he will first connect to the TCP wrapper.
TCP_wrappers is probably on your Linux system, so I won't go into the process of compiling them from scratch in this article. See the sidebar “Where to Get TCP_wrappers” for more information on how to download and install TCP_wrappers.
As I mentioned above, the TCP_wrappers daemon tcpd gets its instructions on whether to allow or deny access from the two files /etc/hosts.allow and /etc/hosts.deny. tcpd first scans /etc/hosts.allow for “rules” that match the particular service and computer host name, then searches /etc/hosts.deny. If no match is found, access is allowed. By default, most distributions ship TCP_wrappers “Completely Open”, i.e., the files /etc/hosts.allow and /etc/hosts.deny are empty, allowing access to all Internet services on your machine to everyone.
Before you configure TCP_wrappers, you must decide whether you want your machine to be “Mostly Open” or “Mostly Closed”. Mostly Open means that most services are available to most other computers on the Internet; this is useful for blocking access to just a few troublesome sites or to close off one or two services. Mostly Closed means that most services are closed off to most other computers. For personal computers with normal at-home usage, Mostly Closed is probably the way to go, and it is certainly the more secure option.
Let's look at a “Mostly Open” configuration first. Since we are allowing most connections, the /etc/hosts.allow file is left empty. In the /etc/hosts.deny file, let's put in a rule to deny access to telnet and rlogin to anyone coming from the machine nasty.badguy.net and anyone in the domain cracker.org. The requisite line in /etc/hosts.deny would be:
in.telnetd in.rlogind : nasty.badguy.net \ .cracker.org
Note the leading “.” in front of cracker.org. It signals tcpd to deny access to any machine in that Internet domain. Since the crackers at these sites probably know how to exploit network services other than telnet and rlogin, you can deny access to all services using the wild card ALL:
ALL : nasty.bad-guy.com .cracker.orgOther wild cards that can replace specific host names include LOCAL, UNKNOWN, KNOWN and PARANOID. LOCAL matches any name without a “.” in it, i.e., host names in your local domain. KNOWN and UNKNOWN refer to hosts either found or not found in the Domain Name Service, respectively. PARANOID matches any host whose name does not match its Internet address. This option is not often used, since the wrappers are compiled to reject access to any host that matches this condition before checking the hosts.allow and hosts.deny files. To allow access to all network services to machines in our local domain, we can put the following line in /etc/hosts.allow:
ALL : LOCALNow let's look at a “Mostly Closed” configuration. Remember that hosts.allow is checked first, then hosts.deny and, finally, access is allowed only if no match is found in hosts.deny. To close all services to all outside machines, we use the following rule in our hosts.deny file:
ALL : ALLIn hosts.allow we list only those specific services we want others to use. Of course, we still want to access all of our own services on our own machine. Suppose that we also want to telnet into our machine from a shell account provided by our Internet Service Provider at my.isp.net, and we want to allow anyone to finger our accounts. The rules to put in the /etc/hosts.allow file are:
ALL : localhost in.telnetd : my.isp.net in.fingerd : ALLNow, if we would also like to keep the crackers from cracker.org from using finger to get information about us, we can modify this:
ALL : localhost in.telnetd : my.isp.net in.fingerd : ALL EXCEPT .cracker.orgAs you can see, there is quite a bit of flexibility—but with this flexibility comes the possibility of confusion and, even worse, error. If the configuration files for TCP_wrappers are wrong, you may think you are secure, when in fact you are not. To check your configuration and test its protection, Wietse Venema provided two additional programs: tcpdchk and tcpdmatch.
tcpdchk checks the configuration files for any problems. It can tell if you have used wild cards like ALL or LOCAL incorrectly, if there are nonexistent host names in the access rules, if there are rules for services controlled by tcpd in the /etc/inetd.conf file and much more. For example, the output from tcpdchk for the above Mostly Closed configuration on my machine yielded the following information:
# tcpdchk -v Using network configuration file: /etc/inetd.conf >>> Rule /etc/hosts.allow line 6: daemons: ALL clients: localhost access: granted >>> Rule /etc/hosts.allow line 7: daemons: in.telnetd clients: my.isp.net warning: /etc/hosts.allow, line 7: my.isp.net: \ host not found access: granted >>> Rule /etc/hosts.allow line 8: daemons: in.fingerd clients: ALL EXCEPT .cracker.org access: granted >>> Rule /etc/hosts.deny line 10: daemons: ALL clients: ALL access: denied
I used the -v switch for tcpdchk to generate more verbose output. Note that the program says my.isp.net was not found, which is perfectly true, since it is a host name made up for this example. Also, note that I did not get a similar message for the equally fictitious .cracker.org. That is because it is for an entire domain, and tcpdchk doesn't check if a domain is registered, but rather if a particular host name is in the DNS.
tcpdmatch tests your configuration against a virtual request for an Internet connection. You provide the name of the daemon and a host name, and it tells you whether that connection would be allowed or denied. For example, if I would like to know if the webmaster at www.linuxjournal.com can finger users on my system, I would enter the following:
# tcpdmatch in.fingerd email@example.com client: hostname www.ssc.com client: address 18.104.22.168 client: username webmaster server: process in.fingerd matched: /etc/hosts.allow line 8 access: granted
Note that tcpdmatch found the real host name of www.linuxjournal.com to be www.ssc.com and reports its Internet address. The last line tells me that finger is indeed allowed from this host.
In Practical UNIX and Internet Security, Second Edition by S. Garfinkel & G. Spafford, O'Reilly & Associates, 1996, the authors state:
Programs like tcpdchk and tcpdmatch are excellent complements to the security program tcpwrapper, because they help you head off security problems before they happen. Wietse Venema is to be complimented for thinking to write and include them in his tcpwrapper release; other programmers should follow his example.
I wholeheartedly agree.
TCP_wrappers write a message into your system logs every time a connection is requested, whether it is granted access or not. These entries in the logs above are reason enough to have TCP_wrappers installed on your system. The messages are written through the standard syslog facility and by default go to the same place as mail transactions. On my Linux distribution, Caldera Network Desktop based on Red Hat Linux, the default has been changed so that the messages are written to the same log file as other daemons (LOG_DAEMON facility).
In any event, when someone accesses my machine via telnet, a message like this is placed in the /var/log/messages:
Apr 9 17:24:58 ads in.telnetd: connect from somewhere.else.com
If the connection was refused, the message would read:
Apr 9 17:25:15 ads in.telnetd: refused connect from someother.place.comIf I want to see all the telnet attempts in my log, I can simply type the command:
grep telnetd /var/log/messagesTCP_wrappers can give me even more information through the use of “booby traps”. TCP_wrappers can be configured to run shell commands when certain services are requested. Let's assume I have reason to suspect someone at nasty.badguy.com is trying to use the Trivial FTP program (TFTP) to steal my password file. In my /etc/hosts.deny file, I can put the following line (this example is straight from the hosts_access(5) man page that comes with TCP_wrappers):
in.tftpd : nasty.bad-guy.com : ( /usr/sbin/safe_finger -l @%h |\ /bin/mail -s %d->%h root) &Access to TFTP is denied to all users from nasty.badguy.com. In addition, the command:
safe_finger @nasty.badguy.comis run, and the results are piped into a mail message sent to the root user with the subject line:
in.tftpd->nasty.bad-guy.comsafe_finger is a command provided along with the TCP_wrappers that strips out any “bad” characters, like control sequences and data overruns. Running safe_finger @hostname generates a list of everyone currently logged into that system. The strings %h and %d are called expansions, and tcpd replaces them with the corresponding text for the host name and daemon process, respectively. Other expansions include %a for the client Internet address and %u for the client user name.
Now, this isn't a perfect solution, since our cracker friend may have disabled his finger service or altered it to give false information; however, this example does show us the power of the TCP_wrappers program.
The access control language in the /etc/hosts.allow and /etc/hosts.deny files is quite simple, yet powerful, but TCP_wrappers can be compiled to include even more powerful extensions to the normal access controls. Instead of having the line:
service : host
in the configuration files, you can have the line:
service : host : option : option ...Where option can be allow, deny or many other advanced options.
To enable the advanced options, compile the wrapper programs with the -DPROCESS_OPTIONS. The wrappers are compiled in this way by my Caldera/Red Hat distribution. To check your distribution for the advanced options, run tcpdchk<\!s>-a. On my system, I see the following output:
warning: /etc/hosts.allow, line 6: implicit "allow" at end of rule warning: /etc/hosts.allow, line 7: my.isp.net: host not found warning: /etc/hosts.allow, line 7: implicit "allow" at end of rule warning: /etc/hosts.allow, line 8: implicit "allow" at end of rule
The message implicit<\!s>"allow" indicates my version of the wrappers is looking for additional options in the /etc/hosts.allow file. If your distribution does not have PROCESS_OPTIONS compiled in, you will not see this message.
Using the advanced options, we can do away with the /etc/hosts.deny file entirely, since the options for "allow" and "deny" can be added to any rule. We can change the logging level, control the priority (“niceness”) of the network service, look up user names with the RFC 931 “ident” protocol and display customized “banners” for each service.
More information on these advanced features can be found in the hosts_options(5) man page included with TCP_wrappers, or in the Garfinkel & Spafford book cited above (a must-read for anyone concerned with network security). For now, let's convert our /etc/hosts.allow and /etc/hosts.deny files to use the advanced options. The etc/hosts.deny file is no longer needed. In /etc/hosts.allow we rewrite the rules as follows:
ALL : localhost : allow in.telnetd : my.isp.net : allow in.fingerd : ALL EXCEPT .cracker.org : allow in.tftpd : nasty.bad-guy.com : spawn \ (/usr/sbin/safe_finger -l @%h |\ /bin/mail -s %d-%h root) & : deny ALL : ALL : deny
In English, this says the following:
All services from our own machine are allowed.
telnet is allowed from my.isp.net.
finger is allowed from everywhere except hosts in the cracker.org domain.
tftp is not allowed from nasty.badguy.com, and if they try, we will spring a “booby trap” to find the guilty party.
All other services from everywhere else are denied.
As we have seen, TCP_wrappers provide a simple and effective means to control access to our machines. However, we must still remember “There is no secure in computer security, only more secure or less secure.” As with all security measures, TCP_wrappers have their limitations.
First and foremost, wrappers cannot control access for those services started at boot-time and run until system shutdown. Services like sendmail and httpd (the World Wide Web server) fit this category. These services are always listening to their own ports and require their own access controls. Discussions of the security of sendmail and the World Wide Web fill entire volumes and are certainly outside the scope of this article.
TCP_wrappers may also be vulnerable to “host name spoofing”. Services like rsh and rlogin depend on the host name being correct. If you use a DNS server on which you cannot look up host names, it is possible for an attacker to “spoof” the name lookup by hiding his computer's name behind one your machine “trusts”. You can thwart these attacks by putting an entry for the Internet address and host name in your local /etc/hosts file, so that you do not depend on outside DNS lookups (an added benefit is that host name lookups are a lot faster). Be aware that you are now responsible for keeping the /etc/hosts file up to date. If a computer in the /etc/hosts file changes its Internet address, access will be denied until you change its entry. Fortunately, this is a rare event, and I regularly put entries in my /etc/hosts file for computers I contact often and for every host allowed access to my machine.
TCP_wrappers also do some additional homework to avoid name spoofing attacks. When compiled with the default option PARANOID (see the discussion of wild cards above), the wrappers not only check an Internet address by looking up its name but also by looking up its address. If the two don't match, access is automatically denied.
Another vulnerability can come from “source routing”, a situation where a computer from some “outside” address claims to be a trusted computer on the “inside”. TCP_ wrappers can be compiled with KILL_IP_OPTIONS to disable source routing. Luckily, we Linux users generally do not have to worry about this sort of attack, since IP source routing is turned off by default in the kernel itself.
Finally, even though you can use wrappers to control access to certain services, the best way to avoid exploitation of a service is to completely shut it off from the beginning. If you have no use for rsh or rlogin, edit your /etc/inetd.conf file and put a hash mark, #, in front of the lines for the shell and login services. This advice goes for any other service you don't use. Security holes cannot be exploited on services that are never started. “When in doubt, comment it out” is my motto.
TCP_wrappers are cheap and effective tools for controlling access to your Linux computer. Even without employing the access control features of wrappers, the ability to trace each and every connection to your machine through your system logs can be extremely valuable. TCP_wrappers can control access with a broad brush or a single pen stroke. Either way, I hope this article has raised your awareness of the ease with which you can control the “network face” of your machine.