Section 12.5. Access Control

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.

12.5.1 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
12.5.1.1 tcpd access control files

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 reasonperhaps 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:

KNOWN

The result of the test is KNOWN when the remote system returns a username in response to the query.

UNKNOWN

The result of the test is UNKNOWN when the remote host does not run identd and thus fails to respond to the query.

ALL

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.

12.5.1.2 Defining an optional shell command

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.

12.5.1.3 Optional access control language extensions

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.

12.5.2 Controlling Access with xinetd

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:

USERID

Logs the user ID of the remote user. USERID can be logged for both successful and failed connection attempts.

HOST

Logs the address of the remote host. Like USERID, HOST can be used for both success and failure.

PID

Logs the process ID of the server started to handle the connection. PID applies only to log_on_success.

DURATION

Logs the length of time that the server handling this connection ran. DURATION applies only to log_on_success.

EXIT

Logs the exit status of the server when the connection terminates. EXIT applies only to log_on_success.

ATTEMPT

Logs unsuccessful connection attempts. ATTEMPT applies only to log_on_failure.

RECORD

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 appliesfor 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.