Cover V11, I03

Article
Figure 1
Figure 2
Figure 3

mar2002.tar

Securing Public-Access Networks: Stopping the IP Thieves

Walt Jones

When I was a student at the University of Puget Sound in Tacoma, Washington, I worked in the ResNet office as a Senior ITT consultant. The ResNet office handles all aspects of student networked computing on campus. Particularly, the office is responsible for the pay-for-service Ethernet network that currently includes 9 subnets covering 9 dorms, 8 Greek houses, and 78 university-owned residential houses for a total of 1450 student connections. This IP-based network not only allows users to access university-only Intranet resources, but also the external Internet.

The Problem

Until two years ago, the primary means of connecting students to our network was to physically hand them IP assignments and manually configure their machines. This paper-intensive approach generated a number of problems for our office. The problems fall under three categories:

1. Static user data -- In a university setting, students don't stay in one place for very long. Students change rooms, study abroad, leave university-owned housing, and sometimes even leave the university all together during the course of a semester. Because we had no way to sync with university data, we could do little more than rely on word-of-mouth to update us on a student's whereabouts.

2. Manual client configuration -- All students connected to our network were able to do so only because a technician from our office had manually configured their machines. Despite our scheduling efforts, long lines were common as students tried to connect to ResNet.

3. IP theft -- This category is two-fold. Because we relied on manual configuration, students knowledgeable enough to configure their own machines could simply "borrow" an IP from another student and use it as their own. Not only did this cause IP conflicts and connection problems for other students, it took scarce revenue away from our office.

The Solution

Obviously, something needed to be done not only to lock down the network, but also to alleviate the workload placed on our administrators and consultants. ISC DHCP (http://www.isc.org) was an obvious choice to enable dynamic configuration of clients on the network. However, DHCP alone would only solve a handful of the problems we needed to address. We really needed a way to extend DHCP so that we could lock down resources and automate user registration while still leveraging DHCP's ability to handle automatic configuration. The solution we found is actually quite simple. The core of our "Autoreg" system relies on using DHCP, DNS, and Access Control Lists on our router to dictate how much access a client can have.

To lock down the network for unregistered users, we configured DHCP so that it hands out IP addresses to unknown clients in a specific range. This IP range is then "wounded", using Access Control Lists on our router and a process we call "DNS spoofing" so that they can only access our Autoreg server (the machine running DHCP and supporting applications). To register clients, we exposed CGI scripts (I'm calling them scripts, but they're actually compiled C++ executables) via an Apache Web server (http://www.apache.org) running on our Autoreg server. These scripts authenticate users and then move them to an unrestricted IP address by placing a static host entry for that machine in the ISC DHCP configuration file (dhcpd.conf) and restarting the DHCP daemon. This approach not only leaves ISC's code completely intact, but allows us to create an entire Web interface for the system by relying on CGI scripts.

To handle user authentication and data storage, we rallied the university for access to the main Oracle database. We created tables on the main Oracle database that would automatically create "accounts" for students who were tagged as living in university-owned housing. These "accounts" were actually links to data stored in tables owned by other departments. Password protection for these accounts is provided via a direct interface with RADIUS, helping us move toward a single sign-on architecture for the campus. Our ability to access the university's database also meant that we had the ability to place service charges directly on students' financial accounts, relieving us of the need to enforce payment. See Figures 1 and 2 for a simplified representation of our core database.

Configuring BIND

The basic idea behind "DNS spoofing" is to have a separate DNS server that returns one IP address for every query received. For our Autoreg system, we chose to install a copy of ISC BIND (http://www.isc.org) on the same machine as our DHCP server that would point only to itself. In turn, we configured our wounded range of IP addresses in DHCP to use our Autoreg server for DNS, automatically directing all queries to our registration site. Because most of our users lack the understanding of TCP/IP to specify a real DNS server manually, this "dummy" DNS essentially restricts access to most IP-network resources. Furthermore, the DNS spoofing enables users to find the registration site easily because all queries are redirected there. Thus, to begin, users simply launch their favorite Web browsers, and our registration site immediately appears.

Configuring ISC BIND to return nothing but a single address is a bit of a hack, but it works. In essence, we simply configured the server to assume that it knew the answer to every address query. Here is the entirety of our host.all file. Our Autoreg server, running DHCP, our dummy BIND, and the registration Web site is 210.207.100.30:

*.       IN     NS     resnet.ups.edu.
*        IN     A      210.207.100.30
resnet   IN     A      210.207.100.30
The key to making this setup work in the broad scheme of things is to ensure that this "dummy" DNS can't propagate to your other DNS servers.

Configuring DHCP

The second step in implementation was to configure DHCP and our router to split networks into wounded and non-wounded IP ranges. Several of our "subnets" are actually shared networks composed of two physical subnets. In these cases, about twenty IPs from the first of the two subnets are "wounded" for unregistered users. The remainder of the first subnet and the whole of the second subnet are available for registered users. For subnets that do not operate as shared networks, about ten IPs are wounded. Normally, dhcpd.conf is set up so that there is one global definition for DNS and NNS servers as well as lease lengths. To implement the first level of wounding for specific IPs, we set up separate definitions for our dummy DNS server, that override the global definitions. The following is an excerpt from our dhcpd.conf file where we define the configuration for our Greek-row subnet. Again, our Autoreg server, running DHCP, our dummy DNS, and the registration Web site is resnet.ups.edu at 210.207.100.30:

### GREEK ROW
### 210.207.118.0 and 210.207.119.0
shared-network greek-row
{
      # Used for wounded IPs and for registered users
      subnet 210.207.118.000 netmask 255.255.255.000
      {
            option routers 210.207.118.001;

            # Wounded IPs - DNS and lease times specified to 
            # override global definitions
            pool
            {
                  range 210.207.118.020 210.207.118.029;
                  option domain-name-servers 210.207.100.030;
                  option netbios-name-servers 210.207.100.030;
                  default-lease-time 600;
                  max-lease-time 600;
                  allow unknown clients;
            }
}

# Used for registered users only, therefore we're not
# specifying DNS, lease times or lease ranges
subnet 210.207.119.000 netmask 255.255.255.000
{
      option routers 210.207.119.001;
}
}
Any static host entries outside of the wounded range that are created for registered users will default to the global DNS, NNS, and lease time definitions. Since registered users are presumed to remain registered for a long period of time, our global lease length is several days in length.

Configuring the Router

Most students use our network for Web and email access, so giving unregistered users our dummy DNS is enough to wound the IPs and limit user access. However, because there are always students who work hard to get around any security system on a college campus, we wanted to make sure that users who were smart enough to manually specify a real DNS server wouldn't be able to skirt our system. To do this, we created access control lists on our router that would allow access to only the DHCP server. For example, to wound 210.207.118.20 so that it can access the registration Web server (port 80 on 210.207.100.30) and nothing else, we would add the following lines to a Cisco router:

access-list 118 permit tcp host 210.207.118.20 host 210.207.100.30 eq 80
access-list 118 deny ip host 210.207.118.20 any
These statements lock down this IP and make anyone who uses it unable to do anything except enjoy our registration site. While this approach is an excellent way to limit access, there are performance issues on the router to consider. Because all Access Control Lists must be processed on a per-packet basis, we try to keep our pool of wounded IPs to a minimum.

The router was also used to dictate which subnets would be governed by the Autoreg system. Much of the campus already ran DHCP, and we wanted to ensure that implementing the new system would not affect users elsewhere on campus. To do this, we created individual IP helpers on the router for each subnet on our campus. For example, adding the following lines to a Cisco router specifies 210.207.100.30 (our Autoreg server) as the DHCP server for interface 1 and 210.207.100.69 (our general DHCP server) as the DHCP server for interface 2:

ip forward-protocol udp
!
interface ethernet 1
ip helper-address 210.207.100.30
interface ethernet 2
ip helper-address 210.207.100.69
This approach also enabled us to retain our goal of having one Autoreg server handle the entire ResNet network.

Writing the Code

Creating a wounded range of IP addresses and spoofing users with a dummy DNS is all well and good, but these measures don't let you do a whole lot unless you can easily move machines between the wounded and non-wounded ranges. This is where the CGI scripts come into play. For the sake of simplicity, I will only address the process that registers a user on the system. After this is implemented, it is easy to add other utilities that handle IP management, system configuration, and reporting capabilities. Because we needed to support Oracle database connections and integrate RADIUS authentication, we implemented all of our CGI scripts in C++. Doing CGI in C++ isn't easy by itself, but there are libraries readily available that make it just as easy as in Python or Perl. We used a C++ library I developed that was inspired by Thomas Boutell's CGIC library (http://www.boutell.com/cgic).

The first hurdle was getting our CGI registration application to talk to RADIUS. Elsewhere on campus, a group was working with a Web-proxy caching product called Squid that had an available RADIUS authenticator written by Marc van Selm (http://selm.www.cistron.nl/~selm/authtools). We borrowed the source code for this and tweaked it so that it would work as an external function call from within our registration application. We also had to thread the library to fire off multiple requests since about half of our UDP packets would get lost on the way to the RADIUS server. Ultimately, we were able to simply pass a username and password to the function and it would return a pass, fail, or timeout result.

The second major issue was that Pro*C++ (our Oracle connection library for C++) required specific environment variables to create database connections. Because our application ran inside a CGI environment, we could not set the necessary environment variables through login scripts or batch files. Ultimately, we had to put code into our application that would create the required environment variables each time it was executed, which ensured that regardless of the environment, all necessary Oracle environment variables would exist.

After authentication and database connectivity were sufficiently working, we tackled the process of moving a client from one IP to another. To create a static host definition in dhcpd.conf, we needed the MAC address of the client. Fortunately, ISC DHCP tracks the MAC address when it gives out a new lease:

lease 210.207.118.24 {
  starts 6 2001/05/12 09:48:59;
  ends 6 2001/05/12 09:58:59;
  tstp 6 2001/05/12 09:58:59;
  binding state free;
  hardware ethernet 00:48:54:88:bf:d8;
  uid "\001\000HT\210\277\337";
  client-hostname "ratbert";
}
Since leases are only used for our wounded IP range, we could efficiently parse through the lease file, dhcpd.leases, and pull out the "hardware ethernet" entry associated with a specific IP. Thus, to get the MAC address of the client we want to register, we just pull the IP out of the CGI environment and then parse the lease file for the client's hardware address. From there, our application asks the database for the next available non-wounded IP in the user's subnet and then creates a static host entry in the dhcpd.conf file:

host 1239 {
  hardware ethernet 00:48:54:88:bf:d8;
  fixed-address 210.207.119.56;
}
As mentioned before, the one caveat with this approach is that the DHCP daemon must be restarted for it to pick up any changes to dhcpd.conf. Rather than deal with permissions in our registration application, we created a simple Perl script that reads the PID from dhcpd.pid, kills that process, and then restarts dhcpd. The script then sleeps for a specified amount of time before looping. The sleep time can be adjusted depending on the system load. For example, during times when registration frequency is high, such as the beginning of the school year, we set it to restart the DHCP daemon more often than periods when few changes are being made.

Code for this system was divided into individual components that correspond to the respective daemons, system files, environment interfaces, and Web page creation. The one caveat in this approach was that the structure of Pro*C++ is such that it is not easily componentized. Instead, we opted to create one Pro*C++ source file for each top-level "utility" in our system (e.g., client registration, user lookup, IP administration, etc.) so that Oracle's global variables would not step on each other. Our registration code consists of these files:

.pc -- A file containing all necessary Pro*C++ code as well as the utility's main() function. In this article, we deal only with register.pc.

regpage.js -- Contains JavaScript used in form validation for the primary registration page.

rad_auth.c -- Contains the authenticate() function used for RADIUS authentication.

cgiinterface.h -- Defines the cgiInterface object class used for CGI interaction.

conf.h -- Contains basic configuration for paths and files used by the Autoreg system.

configfile.h -- Defines the configFileInterface object class used to interact with the Autoreg configuration file (/etc/autoreg.conf in our configuration).

dhcpconf.h -- Defines the dhcpConfInterface object class used to interact with the dhcpd.conf file.

dhcplease.h -- Defines the dhcpLeaseInterface object class used to interact with the dhcpd.leases file.

errorlog.h -- Defines the errorLogInterface object class used to interact with our generic error log file.

filelock.h -- Defines the fileLock object class used to virtually lock files while they are in use by the system.

html_reg_pages.h -- Contains functions that output the HTML pages used for registration.

ipfunctions.h -- Contains functions that process IP addresses.

md5.h -- Required by rad_auth.c for RADIUS authentication.

oraclefunc.h -- Contains functions that support Oracle operations.

radius.h -- Required by rad_auth.c for RADIUS authentication.

radiusd.h -- Required by rad_auth.c for RADIUS authentication.

stringfunc.h -- Contains functions that handle string processing.

sysdep.h -- Required by rad_auth.c for RADIUS authentication.

timeutils.h -- Contains functions that handle system time.

The base code for the registration app (register.pc) itself is fairly straightforward. The process is split into four major sections:

Section 1: Default page -- Section selection is processed by setting the sessionvars CGI form variable. If the variable is not set, it is assumed that the user has not started the registration process. When the user first accesses the registration site, no variables are set, so they are presented with the primary registration page where they enter their usernames and passwords, as well as information about their computers. This is accomplished via a call to showHTMLregInitialPage().

Section 2: User authentication -- Once the user presses the "register" button on the primary registration page, the registration executable is launched with sessionvars set to AUTH. After collecting information from the CGI form, the app grabs the user's current IP address from the CGI environment by calling cgiInterface.getRemoteAddr(). This IP address is then referenced against the dhcpd.leases file using dhcpLeaseInterface.findEntry() to get the user's MAC address. We then check the status of several things, including the user's account permissions and their IP's validity (so that we don't try and register off-campus people who may have wandered onto our registration site). We then authenticate the user against the RADIUS server by making an external call to authenticate(), which returns a success or failure code. Finally, we check to make sure that the MAC address extracted from dhcpd.leases is not already associated with a registered user. Once the user has passed this gauntlet of tests, they are asked to digitally sign the network usage agreement. If they have previously signed this agreement, the app continues on to section 3 by setting sessionvars to REG.

Section 3: Machine registration -- First the app queries the database to find an unused IP in the same subnet as the machine's current IP by calling getUnusedIP(). Next, a new record is created for the machine in the database via a call to addNewMachine(), after which a corresponding host record is created in dhcpd.conf with dhcpConfInterface.addEntry(). Finally, the "registration complete" page is displayed to the user. This page asks users to wait 60 seconds and then reboot their machines. To facilitate this, we placed a pseudo-progress bar on the page that graphically times out the passing of 60 seconds. The progress bar is actually a simple GIF animation, but it provides users with the sense that something is happening during the 60 seconds we wait for the dhcp daemon to cycle and read the updated dhcpd.conf file.

Section 4: Registration complete -- After 60 seconds, the "registration complete" page automatically reloads the registration CGI executable with sessionvars set to COMPLETE. This simply displays a page that tells the users to restart their machines to release and renew the DHCP lease.

Figure 3 shows the basic approach to the registration process.

The Results

The results of implementing this system on our campus were astounding. ResNet administrator and consultant workload been cut by more than 75% and students on campus no longer need to wait in long lines to get online. We now simply include a small ResNet booklet along with other orientation materials that are mailed to students a month before they arrive on campus. The instructions for registering their machines and getting online take up no more than a single page of this booklet. Now, rather than waiting several weeks for a consultant to configure their machines manually, a user only has to follow these simple steps:

1. Connect a computer to the Ethernet jack on the wall of the room.

2. Turn on the computer.

3. Launch a Web browser (with our assumption that most machines are pre-configured for DHCP). The registration page appears.

4. Fill out the online registration form with a campus username and password.

5. Press the "Register" button. First-time users are required to sign an acceptance of a network usage agreement digitally before registration continues.

6. At this point, a screen appears and tells each user that registration is complete. The user is then required to wait 60 seconds for DHCP to restart before restarting his own machine.

Not only has registration been simplified, but also our ability to communicate with the university's Oracle database has provided us with powerful reporting and tracking abilities. Administrators can easily track individual users on the system, and they can change settings for users within seconds. If users become problematic, fines can be added to their accounts or they can be unregistered and locked out of the network.

Loose Ends

While our system has greatly increased network security and has all but stopped IP theft, there are still some extensions to the system that would better protect the network. For example, one loop-hole in our system is DHCP itself. If users have enough knowledge of how our network is set up, they could manually configure their machines to use a non-wounded IP without registering. To combat this, you could create shared networks for each physical subnet that would actually contain two subnets. One would be used exclusively for wounded IPs while the other would contain only registered users. This approach would confuse would-be IP thieves and make it difficult for them to guess which IP addresses allow them unrestricted access to the network.

Another approach to maintaining network security when users manually configure their machines for a non-wounded IP is currently in use at RIT. There, Matt Campbell (http://www.rit.edu/~mrcsys/dhcp/index.html) uses an approach combining VLANs and ARP snapshots to keep unknown MAC addresses from accessing resources. The system works by keeping a database of MAC and IP addresses populated by SNMP calls to the router's ARP data. An SNMP monitoring tool is installed on the DHCP server to capture traps from the router. When the SNMP monitor catches an "interface up" or similar trap, it references the MAC address against the database and determines whether that machine is known or unknown. If it is unknown, the MAC is moved into a VLAN configured to allow access only to the DHCP server.

Conclusion

Even with a few security holes in our Autoreg system, it has all but eliminated IP theft and has allowed our department to shift its focus away from policing the network. Students have benefited greatly from automated registration and are now able to get connected without coming anywhere near our office. I hope this article has provided systems administrators with a good example of how several out-of-the-box applications can be combined to provide a solution to a problem that is not easily solved.

Thanks

Many people were responsible for bringing this system into existence. Among others, I want to recognize Jim Driskell, Leila Kaspersen, Josh Phelps, Jeff Strong, and Dave Wilson at the University of Puget Sound. Additional support was provided by Matt Campbell at the Rochester Institute of Technology and Michael Kazmier at the University of Northern Colorado.

Walt Jones graduated from the University of Puget Sound with a BS in computer science in the spring of 2001. While at the university, he worked three years in the ResNet office, initially as a network technician and later as a Senior ITT Consultant. He was first trained as a network and hardware engineer, but now does most of his work as a software developer for Visual Basic, C++, and Python. Walt currently work as a freelance software developer for several Seattle firms where he designs and implements customized network administration and security tools. Walt can be contacted at: wjones@blarg.net.