Access control is a technique for limiting access. Routers and hosts that use access control check the address of a host requesting a service against an access control list. If the list says that the remote host is permitted to use the requested service, the access is granted. If the list says that the remote host is not permitted to access the service, access is denied. Access control does not bypass any normal security checks. It adds a check to validate the source of a service request and retains all of the normal checks to validate the user.
Access control systems are common in terminal servers and routers.
For example, Cisco routers have an access control facility. Access
control software is also available for Unix hosts. Two such packages are
xinetd
and the TCP wrapper program.
First we examine TCP wrapper (tcpd
),
which gets its name from the fact that you wrap it around a network
service so that the service can be reached only by going through the
wrapper.
The wrapper package performs two basic functions: it logs requests for Internet services, and provides an access control mechanism for Unix systems. Logging requests for specific network services is a useful monitoring function, especially if you are looking for possible intruders. If this were all it did, wrapper would be a useful package. But the real power of wrapper is its ability to control access to network services.
The wrapper software is included with many versions of Linux and
Unix. The wrapper tar
file
containing the C source code and Makefile necessary to build the
wrapper daemon tcpd
is also
available from several sites on the Internet.
If your Unix system does not include wrapper, download the
source, make tcpd
, and then install
it in the same directory as the other network daemons. Edit
/etc/inetd.conf and replace the path to each
network service daemon that you wish to place under access control
with the path to tcpd
. The only
field in the /etc/inetd.conf entry affected by
tcpd
is the sixth field, which
contains the path to the network daemon.
For example, the entry for the finger
daemon in
/etc/inetd.conf on our Solaris 8 system
is:
finger stream tcp6 nowait nobody /usr/sbin/in.fingerd in.fingerd
The value in the sixth field is
/usr/sbin/in.fingerd. To monitor access to the
finger
daemon, replace this value
with /usr/sbin/tcpd, as in the following
entry:
finger stream tcp6 nowait nobody /usr/sbin/tcpd in.fingerd
Now when inetd
receives a
request for fingerd
, it starts
tcpd
instead. tcpd
then logs the fingerd
request, checks the access control
information, and, if permitted, starts the real finger
daemon to handle the request. In this
way, tcpd
acts as a gatekeeper for
other functions.
Make a similar change for every service you want to place under
access control. Good candidates for access control are ftpd
, tftpd
, telnetd
, and fingerd
. Obviously, tcpd
cannot directly control access for
daemons that are not started by inetd
, such as sendmail
and NFS. However, other tools, such
as portmapper
, use the tcpd
configuration files to enforce their
own access controls. Thus the wrapper configuration can have a
positive impact on the security of daemons that are not started by
inetd
.
Using the wrapper on most Linux systems is even easier. There is
no need to download and install the tcpd
software. It comes as an integral part
of the Linux release. You don’t even have to edit the
/etc/inetd.conf file because the sixth field of
the entries in that file already points to the tcpd
program, as shown below:
finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd -w
The information tcpd
uses to
control access is in two files,
/etc/hosts.allow and /etc/hosts.deny . Each file’s function is obvious from its name.
hosts.allow contains the list of hosts that are
allowed to access the network’s services, and
hosts.deny contains the list of hosts that are
denied access. If the files are not found, tcpd
permits every host to have access and
simply logs the access request. Therefore, if you only want to
monitor access, don’t create these two files.
If the files are found, tcpd
checks the
hosts.allow file first, followed by the
hosts.deny file. It stops as soon as it finds a
match for the host and the service in question. Therefore, access
granted by hosts.allow cannot be overridden by
hosts.deny.
The format of entries in both files is the same:
service-list
:host-list
[:shell-command
]
The service-list is a list of network
services, separated by commas. These are the services to which
access is being granted (hosts.allow) or denied
(hosts.deny). Each service is identified by the
process name used in the seventh field of the
/etc/inetd.conf entry. This is simply the name
that immediately follows the path to tcpd
in inetd.conf.
(See Chapter 5 for a description
of the arguments field in the /etc/inetd.conf
entry.)
Again, let’s use finger
as
an example. We changed its inetd.conf entry to
read:
finger stream tcp nowait nobody /usr/etc/tcpd in.fingerd
Given this entry, we would use in.fingerd
as the service name in a
hosts.allow or hosts.deny
file.
The host-list is a comma-separated list
of hostnames, domain names, Internet addresses, or network numbers.
The systems listed in the host-list are granted access
(hosts.allow) or denied access
(hosts.deny) to the services specified in the
service-list. A hostname or an Internet address matches an
individual host. For example, rodent is a
hostname and 172.16.12.2 is an Internet address. Both match a
particular host. A domain name matches every host within that
domain; e.g., .wrotethebook.com matches
crab.wrotethebook.com,
rodent.wrotethebook.com,
horseshoe.wrotethebook.com, and any other hosts
in the domain. When specified in a tcpd
access control list, domain names
always start with a dot (.). A network number matches every IP
address within that network’s address space. For example, 172.16.
matches 172.16.12.1, 172.16.12.2, 172.16.5.1, and any other address
that begins with 172.16. Network addresses in a tcpd
access control list always end with a
dot (.).
A completed hosts.allow entry that grants FTP and Telnet access to all hosts in the wrotethebook.com domain is shown below:
ftpd,telnetd : .wrotethebook.com
Two special keywords can be used in
hosts.allow and hosts.deny
entries. The keyword ALL can be used in the service-list to match all
network services, and in the host-list to match all hostnames and
addresses. The second keyword, LOCAL, can be used only in the host-list. It matches
all local hostnames. tcpd
considers a hostname “local” if it contains no embedded dots.
Therefore, the hostname rodent would match on
LOCAL, but the hostname rodent.wrotethebook.com
would not match. The following entry affects all services and all
local hosts:
ALL : LOCAL
A more complete example of how tcpd
is used will help you understand
these entries. First, assume that you wish to allow every host in
your local domain (wrotethebook.com) to have
access to all services on your system, but you want to deny access
to every service to all other hosts. Make an entry in
/etc/hosts.allow to permit access to everything
by everyone in the local domain:
ALL : LOCAL, .wrotethebook.com
The keyword ALL in the services-list indicates that this rule applies to all network services. The colon (:) separates the services-list from the host-list. The keyword LOCAL indicates that all local hostnames without a domain extension are acceptable, and the .wrotethebook.com string indicates that all hostnames that have the wrotethebook.com domain name extensions are also acceptable.
After granting access to just those systems you want to service, explicitly deny access to all other systems using the hosts.deny file. To prevent access by everyone else, make this entry in the /etc/hosts.deny file:
ALL : ALL
Every system that does not match the entry in
/etc/hosts.allow is passed on to
/etc/hosts.deny. Here the entry denies everyone
access, regardless of what service they are asking for. Remember,
even with ALL in the services-list field, only services started by
inetd
, and only those services
whose entries in inetd.conf have been edited to
invoke tcpd
, are affected. This
does not automatically provide security for any other
service.
The syntax of a standard wrapper access control file can be a little more complicated than the examples above. A hosts.allow file might contain:
imapd, ipopd3 : 172.16.12. ALL EXCEPT imapd, ipopd3 : ALL
The first entry says that every host whose IP address begins with 172.16.12 is granted access to the IMAP and POP services. The second line says that all services except IMAP and POP are granted to all hosts. These entries would limit mailbox service to a single subnet while providing all other services to anyone who requested them. The EXCEPT keyword is used to except items from an all-encompassing service list. It can also be used in the host-list of an access rule. For example:
ALL: .wrotethebook.com EXCEPT public.wrotethebook.com
If this appeared in a hosts.allow file it would permit every system in the wrotethebook.com domain to have access to all services except for the host public.wrotethebook.com. The assumption is that public.wrotethebook.com is untrusted for some reason—perhaps users outside of the domain are allowed to log into public.
The final syntax variation uses the at-sign (@
) to narrow the definition of services or
hosts. Here are two examples:
in.telnetd@172.16.12.2 : 172.16.12.0/255.255.255.0 in.rshd : KNOWN@robin.wrotethebook.com
When the @
appears in the
services side of a rule it indicates that the server has more than
one IP address and that the rule being defined applies only to one
of those addresses. Examples of systems with more than one address
are multi-homed hosts and routers. If your server is also the router
that connects your local network to outside networks, you may want
to provide services on the interface connected to the local network
but not on the interface connected to the outside world. The
@
syntax lets you do that. If the
first line in this example appeared in a
hosts.allow file, it would permit access to the
Telnet daemon through the network interface that has the address
172.16.12.2 by any client with an address that begins with
172.16.12.
The purpose of the @
when
it appears in the host-list of a rule is completely different. In
the host-list, the @
indicates
that a username is required from the client as part of the access
control test. This means that the client must run an identd
daemon. The host-list can test for
a specific username, but it is more common to use one of three
possible keywords:
The result of the test is KNOWN when the remote system returns a username in response to the query.
The result of the test is UNKNOWN when the remote host
does not run identd
and
thus fails to respond to the query.
This setting requires the remote host to return a username. It is equivalent to using KNOWN but is less commonly used.
The final field that can be used in these entries is the
optional shell-command field. When a match occurs for an entry that
has an optional shell command, tcpd
logs the access, grants or denies
access to the service, and then passes the shell command to the shell for execution.
The shell command allows you to define additional processing that is triggered by a match in the access control list. In all practical examples this feature is used in the hosts.deny file to gather more information about the intruder or to provide immediate notification to the system administrator about a potential security attack. For example:
ALL : ALL : (safe_finger -l @%h | /usr/sbin/mail -s %d - %h root) &
In this example from a hosts.deny file,
all systems that are not explicitly granted access in the
hosts.allow file are denied access to all
services. After logging the attempted access and blocking it,
tcpd
sends the safe_finger
command to the shell for
execution. All versions of finger
, including safe_finger
, query the remote host to find
out who is logged into that host. This information is useful when
tracking down an attacker. The result of the safe_finger
command is mailed to the root
account. The ampersand (&
) at
the end of the line causes the shell commands to run in the
background. This is important. Without it, tcpd
would sit and wait for these programs
to complete before returning to its own work.
The safe_finger
program is
provided with wrapper. It is specially modified to be less
vulnerable to attack than the standard finger
program.
There are some variables, such as %h
and %d
, used in the example above. These
variables allow you to take values for the incoming connection and
to use them in the shell process. Table 12-1 lists the variables
you can use.
Table 12-1. Variables used with tcpd shell commands
Variable | Value |
---|---|
%a | The client’s IP address. |
%A | The server’s IP address. |
%c | All available client information, including the username when available. |
%d | The network service daemon process name. |
%h | The client’s hostname. If the hostname is unavailable, the IP address is used. |
%H | The server’s hostname. |
%n | The client’s hostname. If the hostname is unavailable, the keyword UNKNOWN is used. If a DNS lookup of the client’s hostname and IP address do not match, the keyword PARANOID is used. |
%N | The server’s hostname. |
%p | The network service daemon process id (PID). |
%s | All available server information, including the username when available. |
%u | The client username or the keyword UNKNOWN if the username is unavailable. |
%% | The percent character (%). |
Table 12-1 shows
that %h
is the remote hostname
and %d
is the daemon being
accessed. Refer back to the sample shell command. Assume that the
attempted access to in.rshd
came
from the host foo.bar.org. The command passed
to the shell would be:
safe_finger -l @foo.bar.org | /usr/sbin/mail -s in.rshd-foo.bar.org root
The standard wrapper access control syntax is a complete configuration language that should cover any reasonable need. Despite this, there is also an extended version of the wrapper access control language.
If wrapper is compiled with PROCESS_OPTIONS enabled in the Makefile, the syntax of the wrapper access control language is changed and extended. With PROCESS_OPTIONS enabled, the command syntax is not limited to three fields. The new syntax is:
service-list : host-list : option : option ...
The service-list
and the
host-list
are defined in exactly the same
way they were in the original wrapper syntax. The options are new,
and so is the fact that multiple options are allowed for each rule.
There are several possible options:
allow
Grants the requested service and must appear at the end of a rule.
deny
Denies the requested service and must appear at the end of a rule.
spawn
shell-command
Executes the specified shell command as a child process.
twist
shell-command
Executes the shell command instead of the requested service.
keepalive
Sends keepalive messages to the remote host. If the host does not respond, the connection is closed.
linger
seconds
Specifies how long to try to deliver data after the server closes the connection.
rfc931 [
timeout
]
Uses the IDENT protocol to look up the user’s name on
the remote host. timeout
defines
how many seconds the server should wait for the remote host to
respond.
banners
path
Sends the contents of a message file to the remote
system. path
is the name of a
directory that contains the banner files. The file displayed
is the file that has the same name as the network daemon
process.
nice [
number
]
Sets the nice
value
for the network service process. The default value is
10.
umask
mask
Sets a umask
value
for files used by the network service process.
user
user
[.
group
]
Defines the user ID and group ID under which the network service process runs. This overrides what is defined in inetd.conf.
setenv
variable
value
Sets an environment variable for the process runtime environment.
A few examples based on the samples shown earlier will illustrate the differences in the new syntax. Using the new syntax, a hosts.allow file might contain:
ALL : LOCAL, .wrotethebook.com : ALLOW in.ftpd,in.telnetd : eds.oreilly.com : ALLOW ALL : ALL : DENY
With the new syntax there is no need to have two files. The
options ALLOW and DENY permit everything to be listed in a single
file. The first line grants access to all services to every local
host and every host in the wrotethebook.com
domain. The second line gives the remote host
eds.oreilly.com access through FTP and Telnet.
The third line is the same as having the line ALL
:
ALL
in the
hosts.deny file; it denies all other hosts
access to all of the services. Using the ALLOW and DENY options, the
command:
ALL: .wrotethebook.com EXCEPT public.wrotethebook.com
can be rewritten as:
ALL: .wrotethebook.com : ALLOW ALL: public.wrotethebook.com : DENY
The shell command example using the original syntax is almost identical in the new syntax:
in.rshd : ALL: spawn (safe_finger -l @%h | /usr/sbin/mail -s %d - %h root) & : DENY
A more interesting variation on the shell command theme comes
from using the twist
option.
Instead of passing a command to the shell for execution, the
twist
command executes a program
for the remote user, but not the program the user expects. For
example:
in.ftpd : ALL: twist /bin/echo 421 FTP not allowed from %h : DENY
In this case, when the remote user attempts to start the FTP
daemon, echo
is started instead.
The echo
program then sends the
message to the remote system and terminates the connection.
The extended wrapper syntax is rarely used because everything
can be done with the traditional syntax. It is useful to understand
the syntax so that you can read it when you encounter it, but it is
unlikely that you will feel the need to use it. An alternative to
wrapper that you will encounter is xinetd
. It replaces inetd
and adds access controls. The basics
of xinetd
are covered in Chapter 5. Here we focus on the access
controls that it provides.
As noted in Chapter 5, most of
the information in the xinetd.conf file parallels
values found in the inetd.conf file. What
xinetd
adds are capabilities
similar to those of wrapper. xinetd
reads the /etc/hosts.allow and
/etc/hosts.deny files and implements the access
controls defined in those files. Additionally, xinetd
provides its own logging and its own access controls. If your system uses
xinetd
, you will probably create
hosts.allow and hosts.deny
files to enhance the security of services, such as portmapper
, that read those files, and you
will use the security features of xinetd
because those features provide
improved access controls.
xinetd
provides two logging
parameters: log_on_success
and
log_on_failure
. Use these
parameters to customize the standard log entry made when a connection
is successful or when a connection attempt fails. log_on_success
and log_on_failure
accept the following
options:
Logs the user ID of the remote user. USERID can be logged for both successful and failed connection attempts.
Logs the address of the remote host. Like USERID, HOST can be used for both success and failure.
Logs the process ID of the server started to handle the
connection. PID applies only to log_on_success
.
Logs the length of time that the server handling this
connection ran. DURATION applies only to log_on_success
.
Logs the exit status of the server when the connection
terminates. EXIT applies only to log_on_success
.
Logs unsuccessful connection attempts. ATTEMPT applies
only to log_on_failure
.
Logs the connection information received from the remote
server. RECORD applies only to log_on_failure
.
In addition to logging, xinetd
provides three parameters for access
control. Use these parameters to configure xinetd
to accept connections from certain
hosts, paralleling the hosts.allow file, to
reject connections from certain hosts, paralleling the
hosts.deny file, and to accept connections only
at certain times of the day. The three parameters are:
only_from
This parameter identifies the hosts that are allowed to connect to the service. Hosts can be defined using:
a numeric address. For example, 172.16.12.5 defines a specific host, and 129.6.0.0 defines all hosts with an address that begins with 129.6. The address 0.0.0.0 matches all addresses.
an address scope. For example, 172.16.12.{3,6,8,23} defines four different hosts: 172.16.12.3, 172.16.12.6, 172.16.12.8, and 172.16.12.23.
a network name. The network name must be defined in the /etc/networks file.
a canonical hostname. The IP address provided by the remote system must reverse-map to this hostname.
a domain name. The hostname returned by the reverse
lookup must be in the specified domain. For example, the
value .wrotethebook.com
requires a host in the wrotethebook.com
domain. Note that when a domain name is used, it starts with
a dot.
an IP address with an associated address mask. For example, 172.16.12.128/25 would match every address from 172.16.12.128 to 172.16.12.255.
no_access
This parameter defines the hosts that are denied access to
the service. Hosts are defined using exactly the same methods as
those described for the only_from
attribute.
access_times
This parameter defines the time of day a service is
available, in the form hour
:
min
-
hour
:
min
. A
24-hour clock is used. Hours are 0 to 23 and minutes are 0 to
59.
If neither only_from
nor
no_access
is specified, access is
granted to everyone. If both are specified, the most exact match
applies—for example:
no_access = 172.16.12.250 only_from = 172.16.12.0
The only_from
command in this
example permits every system on network 172.16.12.0 to have access to
the service. The no_access
command
takes away that access for one system. It doesn’t matter whether the
no_access
command comes before or
after the only_from
command. It
always works the same way because the more exact match takes
precedence.
A sample POP3 entry from xinetd.conf is shown below:
# default: on # description: The POP3 service allows remote users to access their mail \ # using an POP3 client such as Netscape Communicator, mutt, \ # or fetchmail. service login { socket_type = stream wait = no user = root log_on_success += USERID log_on_failure += USERID only_from = 172.16.12.0 no_access = 172.16.12.231 server = /usr/sbin/ipop3d }
In the sample, the only_from
command permits access from every system on network 172.16.12.0, which
is the local network for this sample system, and blocks access from
all other systems. Additionally, there is one system on subnet
17.16.12.0 (host 172.16.12.231) that is not trusted to have POP
access. The no_access
command
denies access to anyone on the system 172.16.12.231.
Remember that wrapper and xinetd
can only control access to services.
These tools cannot limit access to data on the system or moving across
the network. For that, you need encryption.