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