ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


FreeBSD Basics

Securing BSD Daemons

02/07/2001

Also in FreeBSD Basics:

Fun with Xorg

Sharing Internet Connections

Building a Desktop Firewall

Using DesktopBSD

Using PC-BSD

In last week's article, we used the sockstat utility to determine which daemons were listening for network connections on a FreeBSD system. Let's continue where we left off by taking a closer look at /etc/inetd.conf.

Remember that inetd is the internet super-server which listens for requests on behalf of other daemons; it reads /etc/inetd.conf to determine which ports you wish it to listen on. You should always comment out the lines which represent the daemons you don't wish people to make network connections to. A good general rule is that if you don't know what a daemon does, comment it out.

On my system, inetd is listening for IPV4 connection requests for the following daemons: ftp, telnet, comsat, ntalk, and tftp. I don't intend to maintain a tftp or ftp server, and I'm not totally convinced that I need to provide comsat or ntalk services. However, I do need to telnet into my FreeBSD system on occasion. So, I'll become the superuser, open up /etc/inetd.conf using the vi editor, and add comment characters to the four lines that represent the daemons on whose behalf I don't want inetd to listen. When I'm finished, that portion of my /etc/inetd.conf file will look like

#ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l
telnet stream tcp nowait root /usr/libexec/telnetd telnetd
#shell stream tcp nowait root /usr/libexec/rshd rshd
#login stream tcp nowait root /usr/libexec/rlogind rlogind
#finger stream tcp nowait/3/10 nobody /usr/libexec/fingerd fingerd -s
#exec stream tcp nowait root /usr/libexec/rexecd rexecd
#uucpd stream tcp nowait root /usr/libexec/uucpd uucpd
#nntp stream tcp nowait usenet /usr/libexec/nntpd nntpd
# run comsat as root to be able to print partial mailbox contents w/ biff,
# or use the safer tty:tty to just print that new mail has been received.
#comsat dgram udp wait tty:tty /usr/libexec/comsat comsat
#ntalk dgram udp wait tty:tty /usr/libexec/ntalkd ntalkd
#tftp dgram udp wait nobody /usr/libexec/tftpd tftpd /tftpboot
#bootps dgram udp wait root /usr/libexec/bootpd bootpd

Now let's see what happens if I try to ftp into my FreeBSD computer.

ftp 24.141.117.39
Connected to 24.141.117.39.
220 foo.bar.com FTP server (Version 6.00LS) ready.
Name (24.141.117.39:genisis): anonymous
530 User anonymous unknown.
ftp: Login failed.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>

What happened here? It looks like my FreeBSD system is still accepting ftp connections. We've just discovered that it is not enough to edit /etc/inetd.conf; we must also tell inetd to stop listening on behalf of the daemons we've commented out. We'll have to send a hang up or HUP signal to inetd to make it aware of our changes. In order to do that, we must find out inetd's PID:

ps -acux | grep inetd
root 172 0.0 0.6 1040 784 ?? Is Thu03PM 0:00.52 inetd

Since this process is owned by root, we'll have to become the superuser in order to send it the HUP signal:

su
Password:

kill -1 172
exit

To see if this worked, let's rerun the sockstat utility:

sockstat -4
USER     COMMAND    PID   FD PROTO  LOCAL ADDRESS  FOREIGN ADDRESS      
root     XF86_SVG  3752    0 tcp4   *:6000         *:*                  
root     sshd       185    4 tcp4   *:22           *:*                  
root     sendmail   181    4 tcp4   *:25           *:*                  
root     sendmail   181    5 tcp4   *:587          *:*                  
root     lpd        177    6 tcp4   *:515          *:*                  
root     inetd      172    5 tcp4   *:23           *:*                  
daemon   portmap    152    3 udp4   *:111          *:*                  
daemon   portmap    152    4 tcp4   *:111          *:*                  
root     syslogd    149    4 udp4   *:514          *:*                  
root     dhclient   125    3 udp4   *:*            *:*                  
root     dhclient   125    6 udp4   *:68           *:*

Notice that there is now only one entry for inetd and that it is only monitoring port 23, the telnet port. Let's see what happens now if I try to ftp into my FreeBSD system:

ftp 24.141.117.39
ftp: connect: Connection refused

To be thorough, we should also see if inetd is willing to accept connections from IPV6 clients:

sockstat -6
USER   COMMAND  PID  FD PROTO  LOCAL ADDRESS  FOREIGN ADDRESS
root   sshd     185   3 tcp46  *:22           *:*                  
root   inetd    172   9 tcp6   *:21           *:*                  
root   inetd    172  10 tcp6   *:23           *:*

In my example, I have no need to accept ftp (port 21) or telnet (port 23) connections from clients running IPV6, so I'll comment out these two lines in my /etc/inetd.conf file. The IPV6 section is located towards the bottom third of the file. Again, if you edit this file, don't forget to send the HUP signal and check that your changes took effect by running sockstat again.

Editing /etc/inetd.conf allows you to control which ports inetd will monitor for incoming connection requests. However, it doesn't have a mechanism to allow control over which clients you are willing to accept connection requests from. As it stands now, any person with a network connection, which unfortunately includes an Internet connection, to my FreeBSD system can telnet to my computer. Fortunately, they'll be prompted to login, which is one reason why it is important to implement a good password policy. But it makes sense to add another layer of security by telling inetd to refuse the incoming connection request if it does not come from a client that I trust.

This is where tcp wrappers comes into play. If you are running FreeBSD 3.2 or higher, it is already built into your system and is just waiting for you to configure which clients you are willing to accept connection requests from. The name of its configuration file is /etc/hosts.allow, but you'll find the syntax of the file under man 5 hosts_access and additional options under man 5 hosts_options.

There are two things to keep in mind when editing this file:

The syntax itself is fairly straightforward; each rule has 2 fields and can use an optional third field:

daemon: client  :command

If your rule doesn't fit on one line, use the \ character at the end of the line to indicate that the rule continues to the next line.

There are a few useful wildcards and operators that you should be aware of.

As you create your rules, you'll want to use the tcpdmatch utility to ensure that your rules are actually creating the access control you intend. Before I start creating access rules for the telnet daemon on my FreeBSD system, let's see what happens when I try to telnet into my FreeBSD box.

telnet 24.141.117.39
Trying 24.141.117.39...
Connected to genisis.
Escape character is '^]'.

FreeBSD/i386 (genisis) (ttyp1)

login: genisis
Password:
Last login: Thu Jan 18 17:44:29 on ttyv4
Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994
    The Regents of the University of California.  All rights reserved.

FreeBSD 4.2-RELEASE (SOUND) #0: Tue Dec 12 20:01:29 EST 2000

  _   _   _   _   _     _   _   _   _   _   _   _   _   _   _   _  
 / \ / \ / \ / \ / \   / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ 
( M | e | r | r | y ) ( C | h | r | i | s | t | m | a | s | ! | ! )
 \_/ \_/ \_/ \_/ \_/   \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ 
You have mail.

genisis@~ logout
Connection closed by foreign host.

Notice that my system is accepting telnet connections and responds by giving a login prompt on terminal ttyp1. Once I've logged in, I receive my MOTD, my customized prompt, and it's just like I'm sitting at my FreeBSD box. It would appear that inetd did its job properly. Now, let's see what the default /etc/hosts.allow file looks like.

more /etc/hosts.allow
#
# hosts.allow access control file for "tcp wrapped" applications.
# $FreeBSD: src/etc/hosts.allow,v 1.8.2.3 2000/07/20 15:17:44 ume Exp $
#
# NOTE: The hosts.deny file is deprecated.
#       Place both 'allow' and 'deny' rules in the hosts.allow file.
#	See hosts_options(5) for the format of this file.
#	hosts_access(5) no longer fully applies.

#	 _____                                      _          _ 
#	| ____| __  __   __ _   _ __ ___    _ __   | |   ___  | |
#	|  _|   \ \/ /  / _` | | '_ ` _ \  | '_ \  | |  / _ \ | |
#	| |___   >  <  | (_| | | | | | | | | |_) | | | |  __/ |_|
#	|_____| /_/\_\  \__,_| |_| |_| |_| | .__/  |_|  \___| (_)
#					   |_|                   
# !!! This is an example! You will need to modify it for your specific
# !!! requirements!


# Start by allowing everything (this prevents the rest of the file
# from working, so remove it when you need protection).
# The rules here work on a "First match wins" basis.
ALL : ALL : allow

Let's stop right here to see if we can determine why that first rule prevents the rest of the file from working. It basically says: for all daemons, from all clients, allow access. Because this rule matches all clients, and it's the first match, it will always be used. Let's see what happens if we comment out that first rule. I'll become the superuser, open /etc/hosts.allow using the vi editor, and put a # in front of that first rule, and save my changes.

Now, I'll try the telnet command again:

telnet 24.141.117.39
Trying 24.141.117.39...
Connected to genisis.
Escape character is '^]'.
You are not welcome to use telnetd from biko.
Connection closed by foreign host.

Looks like I've effectively blocked all telnet connections to my system. Let's take a look at the rest of the /etc/hosts.allow to see where to go from here to allow limited access via telnet.

Continuing with more of /etc/hosts.allow:

# Wrapping sshd(8) is not normally a good idea, but if you
# need to do it, here's how
#sshd : .evil.cracker.example.com : deny 

# Prevent those with no reverse DNS from connecting.
ALL : PARANOID : RFC931 20 : deny

# Allow anything from localhost.  Note that an IP address (not a host
# name) *MUST* be specified for portmap(8).
ALL : localhost 127.0.0.1 : allow
ALL : my.machine.example.com 192.0.2.35 : allow

# To use IPv6 addresses you must enclose them in []'s
ALL : [fe80::%fxp0]/10 : allow
ALL : [fe80::]/10 : deny
ALL : [3ffe:fffe:2:1:2:3:4:3fe1] : deny
ALL : [3ffe:fffe:2:1::]/64 : allow

# Sendmail can help protect you against spammers and relay-rapers
sendmail : localhost : allow
sendmail : .nice.guy.example.com : allow
sendmail : .evil.cracker.example.com : deny
sendmail : ALL : allow

# Exim is an alternative to sendmail, available in the ports tree
exim : localhost : allow
exim : .nice.guy.example.com : allow
exim : .evil.cracker.example.com : deny
exim : ALL : allow

# Portmapper is used for all RPC services; protect your NFS!
# (IP addresses rather than hostnames *MUST* be used here)
portmap : 192.0.2.32/255.255.255.224 : allow
portmap : 192.0.2.96/255.255.255.224 : allow
portmap : ALL : deny

# Provide a small amount of protection for ftpd
ftpd : localhost : allow
ftpd : .nice.guy.example.com : allow
ftpd : .evil.cracker.example.com : deny
ftpd : ALL : allow

# You need to be clever with finger; do _not_ backfinger!! You can easily
# start a "finger war".
fingerd : ALL \
	: spawn (echo Finger. | \
	 /usr/bin/mail -s "tcpd\: %u@%h[%a] fingered me!" root) & \
	: deny

# The rest of the daemons are protected.
ALL : ALL \
	: severity auth.info \
	: twist /bin/echo "You are not welcome to use %d from %h."

Notice that there aren't any rules that specifically mention telnetd. The last rule in the file is intended to cover all the left over daemons that didn't match earlier rules. Notice that the last rule allowed the connection, but closed it after echoing a message, which is what we saw when I attempted the telnet connection. The %d was replaced by the name of the daemon (telnetd), and the %h was replaced by the hostname of the client trying to connect (biko).

We could have predicted this outcome if we had used the tcpdmatch utility. The syntax to use this utility is very simple:

tcpdmatch daemon_name host_name_of_client

You do not have to be the superuser to run this utility. Let's see what it says will happen if the host "biko" tries to connect to the telnetd on my FreeBSD system:

tcpdmatch telnetd biko

client:   hostname biko
client:   address  24.141.117.40
server:   process  telnetd
matched:  /etc/hosts.allow line 77
option:   severity auth.info
option:   twist /bin/echo "You are not welcome to use telnetd from biko."
access:   delegated

This is very useful output as it tells us which line number in /etc/hosts.allow contains the matching rule and what the result of that rule will be for that client.

Let's modify /etc/hosts.allow to allow telnetd to accept connections form the hosts "genisis" and "biko", but to disallow connections from any other clients. I'll become the superuser and add the following lines; it doesn't matter where in the file I add the lines as long as they appear before that last rule.

telnetd: biko,genisis :ALLOW
telnetd: ALL :DENY 

I'll then check that my rules work by running tcpdmatch on biko, genisis, and a third host called creed:

tcpdmatch telnetd biko
client:   hostname biko
client:   address  24.141.117.40
server:   process  telnetd
matched:  /etc/hosts.allow line 74
option:   ALLOW 
access:   granted

tcpdmatch telnetd genisis
client:   hostname genisis
client:   address  24.141.117.39
server:   process  telnetd
matched:  /etc/hosts.allow line 74
option:   ALLOW 
access:   granted

tcpdmatch telnetd creed
client:   hostname creed
client:   address  24.141.117.41
server:   process  telnetd
matched:  /etc/hosts.allow line 75
option:   DENY 
access:   denied

Let's see what happens when the host creed tries to telnet into my FreeBSD system:

telnet 24.141.117.39
Trying 24.141.117.39...
Connected to genisis.
Escape character is '^]'.
Connection closed by foreign host.

Notice that I didn't receive any message, as the rule on line 75 was the first match, not the rule on line 77.

We've just scratched the surface of the functionality provided by tcp wrappers, but it should be enough to get you started. Depending on your needs, your rules can range from being very simple to quite elegant. You'll definitely want to check out man 5 hosts.access and man 5 hosts.options to see all the configurable options available.

Dru Lavigne is a network and systems administrator, IT instructor, author and international speaker. She has over a decade of experience administering and teaching Netware, Microsoft, Cisco, Checkpoint, SCO, Solaris, Linux, and BSD systems. A prolific author, she pens the popular FreeBSD Basics column for O'Reilly and is author of BSD Hacks and The Best of FreeBSD Basics.


Read more FreeBSD Basics columns.

Discuss this article in the Operating Systems Forum.

Return to the BSD DevCenter.

Copyright © 2009 O'Reilly Media, Inc.