Subject: Host access philosophy (Was: restricting NFS (and associated
To: Andy Ruhl <acruhl@gmail.com>
From: Steven M. Bellovin <smb@cs.columbia.edu>
List: netbsd-users
Date: 10/09/2006 20:37:44
On Mon, 9 Oct 2006 14:17:20 -0700, "Andy Ruhl" <acruhl@gmail.com> wrote:

> On 10/9/06, Chuck Swiger <cswiger@mac.com> wrote:
> > I would not want to run NFS filesharing on an machine directly
> > connected to the Internet without having a firewall in the way.
> 
> This is probably off topic, but please explain this.
> 
> I assume what you are saying is, due to your previous comments about
> the insecurity of NFS, you wouldn't want to expose those services to
> the internet.
> 
> If it were possible to bind NFS to an IP and not expose them to the
> internet (thereby implying a multi homed host), would your answer be
> the same?

Assuming certain other things -- on NetBSD, sysctl
net.inet.ip.checkinterface -- no, it would not.
> 
> Seems to me that all these operating systems coming with built in
> firewalls are really just avoiding issues rather than solving them...

Yes and no.  There are several separate issues here.

One issue -- frequently a red herring -- is authentication: how do you
*know* you're talking to the right party?  I call that a red herring
because most protocols have some form of authentication, and it's
frequently not too difficult to add it on later.  (Doing it right is
another matter, of course, especially for complex protocols.)

A harder issue is assurance: why do I believe that some protocol and its
implementation are correct?  That's the root of my problem with NFS and
the RPC servers that go with it -- they're complex, have a long history of
bugs, and have a design that's not easy to really secure.  With NFS, for
example (and I'm going back quite a ways; it may have been fixed since I
last studied it in detail), the entire security of the protocol rested on
the confidentiality of the file handle for the root i-node of the exported
file system.  That in turn depended a single random number in the i-node,
since the i-node number of the root was always 2 and the major/minor
device number was predictable or guessable.  The inevitable happened -- it
was discovered that the number wasn't, in fact, very random -- see
http://www.cert.org/advisories/CA-1991-21.html for details.  

We can go further -- NFS represents a bad tradeoff of risk/benefit.  If
there's a flaw, the entire file system is at risk, and there's often no
ongoing authentication.  It's also rarely very useful from afar.  The
conclusion is that it shouldn't be available from the outside.

Finally, there's the issue of protocol complexity.  NFS itself is not, in
fact, that bad in that respect; it has a relatively small number of
relatively simple operations.  It's certainly simpler than some things we
(including me) routinely do allow in, such as MIME email, where (among
other things) every file type and every machine which processes
it represents a separate network service.

The question then is what is the proper way to block things.  That is at
the root of my original question: is there a good, clean way to do so?
Single-host "firewalls" -- which are really nothing of the sort; they're
access control mechanisms for local services -- are a mixed blessing.
They're needed because of all of the services that don't have their own
access control right up front -- and for high assurance, you want to
reject improper messages as soon as possible, before they have a chance to
tickle some other bug.  (That was one of the flaws in many SNMP
implementations -- authentication was described by an ASN.1 structure, but
the flaws were in the SNMP parser (for details, see
http://www.cert.org/advisories/CA-2002-03.html), and could thus be
exploited before authentication). For that reason, IP address *rejection*
is a very useful tool, because it operates very early in the game, when
little else has been done.  (No, it's not foolproof, either -- nothing is.)

Given that, how best to do such rejection?  So-called personal firewalls
are add-ons, because most protocol implementations don't provide an early
rejection mechanism.  NetBSD's inetd does provide such a mechanism for all
of its services (see inetd.conf(5)): you can bind a service to a
particular IP address.  For most purposes, rejection at this level (or
via /etc/hosts.deny) are quite sufficient, though there have been a few
attacks (Land and Teardrop -- see
http://www.cert.org/advisories/CA-1997-28.html) that would have gotten
through this level of filtering but would have been blocked by an IP-level
firewall.

We turn at last to my question.  There appear to be no mechanisms other
than pf or ipf that are available here.  Because of the dynamic port
number assignment used in RPC, pf/ipf don't work very well.  The situation
is far worse if there are some RPC services that I want to allow in and
some that I want to block, since the service definition for RPC doesn't
tell me what port numbers will be assigned to what services.  There are
three basic ways to proceed.

The first is to incorporate access control semantics into rpcbind.  It's
not a horrible solution, in that it provides some protection against
attackers who first query rpcbind to find out what port numbers to
attack.  It does no good at all against someone who does a port-scan.  For
this defense to work, there would have to be some linkage between
rpcbind's access control and pf/ifp's -- when a service registered with
rpcbind, rpcbind would translate its tables into appropriate pf/ipf tables
and make the appropriate kernel changes.  (Note that there's a tiny race
condition here....)  It's theoretically possible, but messy, and it ties
to distinct subsystems together.

The second is to tie access control to the process, rather than the port
number.  Systrace is more or less like that; in the Windows world, the
ZoneAlarm firewall works that way.  It's not a horrible way to proceed,
though it doesn't help nearly as much if a single program needs different
levels of access for different parts of it (permit DNS queries; block
other external acces, for example).

The third is to have some construct analogous to chroot(), where we bind a
network "view" to an application at startup time.  I suspect that this is
the proper answer, but I'm by no means certain of it.  The difficulty here
is complexity of administration -- even if I didn't have five different
applications that needed to have the same view.



		--Steven M. Bellovin, http://www.cs.columbia.edu/~smb