Implementing SOCKS
Matt Ganis
Depending on your perspective (system administrator,
programmer, end
user), firewalls can be either a blessing or a curse:
usually what
you buy in security, you often pay for in convenience
and/or ease
of use.
For some years I ran a traditional dual-homed gateway,
where users
logged in, did their work using vt100 emulation, and
logged out. This
worked well for applications like telnet, gopher, and
archie,
but the picture changed dramatically when Mosaic and
the World Wide
Web (WWW) came to town. Was it possible to allow all
50 or 60 concurrent
users to run this behemoth of a program (on the same
machine!) and
not put the system performance in the dumpster? Along
came SOCKS.
How SOCKS Works
SOCKS is officially classified as an Authenticated Firewall
Traversal
protocol (that's the name of the working group in the
IETF furthering
the protocol). With SOCKS I could allow users to run
TCP-based applications
on their workstations and tunnel the traffic to and
from the Internet
through my firewall. The SOCKS protocol, as discussed
in this article,
was developed by David and Michelle Kolbas (see References).
The current
implementation is maintained by Ying-Da Lee of NEC corporation
and
can be obtained by anonymous ftp from ftp.nec.com
(143.101.113.3) in /pub/security/socks. The current
version
(as of this writing) is version 4.2 pre-release number
3. It's been
built and tested on many platforms, including SunOS,
HP-UX, AIX, and
others (see the README for specifics).
To understand how SOCKS works, it's useful first to
look at a traditional
TCP-based client/server model. Figure 1 diagrams the
networking calls
for such a model. The client first sets up for socket-level
communication
by issuing a socket() call, which returns a socket descriptor
-- typically an integer value. The connect() call then
initiates the TCP session to the server on a given port.
The server
traditionally listens on that port for incoming data,
while the client
writes the data to the port (perhaps with a write()
call)
and listens with a read(). In general, in an open environment,
each client connects directly with and establishes its
own TCP connection
to a server, as in Figure 2.
SOCKS is like a relay between the client and the server.
Using the
same example, the networking flow would look like that
shown in Figure 3.
So in essence, the SOCKS server is really a funnel.
All of the
internal clients connect to the firewall, and the firewall
makes all
of the connections to the Internet on behalf of the
clients,
as in Figure 4.
Building SOCKS
SOCKS has two components: the server (sockd) and the
clients
that connect to the server. When you build the package,
you actually
build the two parts: the server (sockd) and the SOCKS
library
(libsocks.a). The SOCKS library consists of replacements
for
the traditional TCP/IP networking calls connect, bind,
listen, accept and getsockname. The replacement
calls just prepend an "r" to the standard
call (e.g., the
replacement for connect() is rconnect()).
Building the package is relatively straightforward.
Once you have
unpacked the archive, issue a make all to build the
server
(sockd), the sample clients (telnet, ftp,
and finger) and the SOCKS library (libsocks.a). You'll
need to make a few decisions and set some variables
in the main Makefile
based on your platform, but the documentation makes
these steps clear
and easy to manage.
One option that is not in the Makefile is
#define NOT_THROUGH_INETD.
If you #define this, you're indicating that you want
to start
the SOCKS server from the command line rather than run
it from the
inetd process. I prefer to run it through inetd, but
if you have a large /etc/sockd.conf file (explained
below),
you may want to consider this option for performance
reasons. Basically,
for every connection to the SOCKS server, the file /etc/sockd.conf
must be consulted to determine if the connection is
valid or not.
If you run from the command line, this configuration
file is read
once and stored in memory, thus speeding up the performance
a bit.
Implementing SOCKS
The process of modifying your clients to use a SOCKS
gateway is called
"socks-ification" (see the file called How_to_SOCKSIFY,
included in the package). Over the releases of SOCKS,
socksification
has become much easier. With the library, libsocks.a,
built,
you need only replace the basic networking calls (connect,
accept, bind, etc.) with their SOCKS counterparts
(rconnect, raccept, rbind). To make life even
easier, copy from the Makefile the #define line that
defines
connect as Rconnect and accept as Raccept,
then just build your application (it's not necessary
to modify
the source, just recompile and link with the SOCKS library).
Assume, for the sake of this article, a network that
looks like one
in Figure 5. The host that will run the socksified clients
must have
a control file, /etc/socks.conf, on it. This file looks
something
like:
direct 127.0.0.1 255.255.255.255
direct 198.15.15.0 255.255.255.0
direct 198.15.14.0 255.255.255.0
sockd @=198.15.15.3 0.0.0.0 0.0.0.0
When a client application (like a Web browser or telnet)
attempts
to make a connection, it consults the /etc/socks.conf
file
on the host. Once the name of the far end client has
been resolved
to an IP address, the address is compared with those
listed in this
file to determine whether the client should make the
connection through
the SOCKS gateway, or go directly to the server. The
example above
tells this client to connect directly (i.e., don't use
the gateway)
for all internal hosts, and to use the gateway at address
198.15.15.3
for everything else (0.0.0.0).
If you don't have access to the external namespace on
your local nameserver,
you may want to define the environment variable SOCKS_NS
to
point to a nameserver that can resolve names in the
external namespace.
I run a caching-only nameserver on the same machine
where I run my
SOCKS server. You can also define SOCKS_SERVER to point
at
a different server than that defined in /etc/socks.conf.
Setting up and running the SOCKS server isn't difficult:
what you
must remember is that you don't "create" a
firewall merely
by running SOCKS on a machine. SOCKS is a "tunnel"
or small
hole (a diode -- or one-way hole) that you open in your
firewall.
For details on firewalling beyond the scope of this
article, see Cheswick
and Belovin, Firewalls and Internet Security. If you
are running
out of inetd, move the binary to your firewall and edit
/etc/services
to add the following line:
socks 1080/tcp # socks service on port 1080
then edit /etc/inetd.conf to include:
socks stream tcp nowait nobody /usr/local/sockd sockd
You must maintain two configuration files to run SOCKS.
The first
is /etc/sockd.route. This file was put into place for
dual-homed
hosts to enable the system administrator to point (or
route) networks
out through a pre-defined interface. Here's an example
of an /etc/sockd.route
file:
198.15.15.1 198.15.15.0 255.255.255.0
198.15.15.1 198.15.14.0 255.255.255.0
198.15.2.1 0.0.0.0 0.0.0.0
Figure 5 shows the firewall, with network 198.15.2.0
being the Internet
(or un-secure) side, and 198.15.15.0 being the secure
side (Note:
I made up these network addresses; they are not meant
to represent
actual networks. The /etc/sockd.route example above
specifies
that anything in networks 198.15.15.0 and 198.15.14.0
(both with a
mask of 255.255.255.0) should be routed out via interface
198.15.15.1
and that everything else should be routed to the 198.15.2.1
interface
(or the un-secure, Internet side).
The second key configuration file is /etc/sockd.conf.
This
file defines which IP addresses can pass through the
box and which
addresses it can "talk" to. Figure 6 shows
an example of the
/etc/sockd.conf file. These are basic source/destination
filters.
The first rule that matches a particular pattern is
the rule applied
to the packets. For example, according to the first
rule, any packet
with no masking -- i.e., 0.0.0.0 -- that is destined
for the
internal network (198.15.14.0) will be denied -- packets
should
only be destined for the un-secure network adapter.
The same is true for rule number 2 -- anything destined
for the
internal network should be denied.
Rule 3 permits any machine in the 198.15.14.0 network
(applying the
mask 255.255.255.0) access to any machine (0.0.0.0)
on the Internet.
Rule 4 will allow only the machine at 198.15.13.1 (note
the full host
mask) to communicate with any machine on the Internet.
Rule 5 a catchall: deny all access.
Nameservice and Protocol Requirements
There are two important things to remember regarding
a SOCKS setup:
the nameservice and the protocol used by the client
applications.
SOCKS is a TCP relaying protocol. This means that the
only applications
that can use SOCKS are those based on TCP connections
(telnet,
ftp, and WWW are examples; archie, though, uses UDP
and thus
cannot use SOCKS). Clients must have full access to
the Internet DNS
tree. I accomplish this by running bind (or named)
on the firewall as a caching-only nameserver. The named.boot
file is relatively simple:
cache . /etc/named.ca
The /etc/named.ca file contains entries for the
Internet root nameservers, which can be obtained from
the NIC via
anonymous ftp from rs.internic.net, in the file domain/named.root.
Here's a sample:
; last update: Oct 5, 1994
; related version of root zone: 1994100500
;
. 99999999 IN NS NS.INTERNIC.NET.
NS.INTERNIC.NET. 99999999 A 198.41.0.4
. 99999999 NS NS1.ISI.EDU.
NS1.ISI.EDU. 99999999 A 128.9.0.107
. 99999999 NS C.PSI.NET.
C.PSI.NET. 99999999 A 192.33.4.12
. 99999999 NS TERP.UMD.EDU.
Despite these caveats, I think you'll find SOCKS a handy
solution
for your firewalls. If your users are looking for WWW
access, I think
they'll thank you as well.
References
Cheswick, William R., and Steven M. Bellovin. Firewalls
and Internet Security: Repelling the Wily Hacker. Reading,
MA:
Addison-Wesley, 1994. ISBN-0-201-63357-4.
Kolbas, David, and Michelle Kolbas. SOCKS. Available
for anonymous ftp: ftp.ibm.net (/pub/socks/socks.ps).
About the Author
Matt Ganis is currently manager of Internet Server
Design and Integration
group for the IBM Global Network in
White Plains, NY. In his spare time he teaches Astronomy
at Pace University
in Pleasantville, NY. He can be reached at 14 Udell
Ct, Cortlandt
Manor, NY 10566 or mganis@ibm.net.
|