Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 1 Jun 2002 11:55:46 -0400 (EDT)
From:      Robert Watson <rwatson@FreeBSD.ORG>
To:        "M. Warner Losh" <imp@village.org>
Cc:        bfischer@Techfak.Uni-Bielefeld.DE, freebsd-hackers@FreeBSD.ORG
Subject:   Re: sandboxing untrusted binaries
Message-ID:  <Pine.NEB.3.96L.1020601113616.86544F-100000@fledge.watson.org>
In-Reply-To: <20020529.225501.134206046.imp@village.org>

next in thread | previous in thread | raw e-mail | index | archive | help

On Wed, 29 May 2002, M. Warner Losh wrote:

> In message: <20020530025817.GA4390@no-support.loc>
>             Bjoern Fischer <bfischer@Techfak.Uni-Bielefeld.DE> writes:
> : Hello,
> : 
> : OpenBSD has a new interesting feature: systrace. It is a system call
> : policy generator for "sandboxing" untrusted or semi-trusted binaries.
> : 
> : The whole idea looks interesting. The implementation details look
> : relatively simple (read: not too complicated). Anyone interested in
> : having a closer look and maybe porting it?
> : 
> : Or I will try to port it myself if at least one core member says:
> : "Interesting technology, send a patch..."
> : 
> : http://www.citi.umich.edu/u/provos/systrace/
> 
> The SecureBSD folks did something similar to an old version of
> FreeBSD, but had such a restrictive license that no one ever
> investigated merging it into the mainline.

Already sent out some private e-mail, but I figured I'd send it out in
public too.  First, there's a lot of prior art in this space -- for
example, see the generic software wrapper toolkit at
ftp://ftp.tislabs.com/pub/wrappers -- although for a somewhat older
version of FreeBSD, it allows a language-driven restriction of processes
based on system call wrapping.  Unfortunately, this software is under a
more restrictive license than really desirable, making it less useful, but
worth referencing.  Likewise, LOMAC in the base tree uses the same
wrapping approach, although not to provide a general purpose policy
framework.

There are a number of reasons we're spending a lot of time on the
TrustedBSD MAC framework, and opted not to stick with the system call
wrapping approach.  They include: 

- Fine-grained locking.  System call "wrappers" that enforce policies only
  at the ABI and top service layers of the kernel are inherrently subject
  to race conditions in fine-grained (and even less fine-grained) kernels.
  This occurs for two reasons: first, time-of-check-time-of-use issues
  which exist both in both traditional and newer kernel models.  These
  frequently exist when arguments are pulled into the kernel multiple
  times, because a deeper layer of the kernel pulls in the string from
  userland, so the wrapper also pulls the string in.  Through careful
  manipulation, the attacker can force the wrapped security check to occur
  on a different object than the actual request.  Second, as you move
  towards higher parallelism in the kernel, these problems get
  dramatically worse.  Consider process locking.  If the wrapping policy
  and the base kernel both require locks on a target process to perform
  their checks, and the base kernel assumes it doesn't hold the lock, the
  wrapping code has to release the lock before the "real" code can execute
  providing the service.  If parallelism is present, then the protections
  on the process could easily change while the lock isn't held, resulting
  in a time-of-check/time-of-use vulnerability.  The process case would
  require a lot of hard work to implement, but the vnode case is a lot
  easier to exploit, as there are frequently long periods of blocking
  associated with VFS operations, opening up race windows. 

- Object aliasing issues.  Often, you can logically think about the kernel
  as providing access to a series of objects and services.  There are a
  series of service components of the kernel that maintain the objects,
  and objects are usually the granularity of access control (i.e.,
  individual objects have individual protections, and classes of objects
  sometimes share a single set of protections).  Unfortunately (or
  fortunately if you're an application writer), the kernel can offer many
  ways to name the same object -- pathnames, file descriptors, etc.  These
  names tend to change a lot over time, and often objects have multiple
  names even in the same namespace.  This puts system call wrappers in a
  very bad place to consistently enforce policies -- what they'd like to
  do is enforce policies based on the object, and not based on the name. 
  Unfortunately, the authoritative name->object conversion is sometimes
  done deep in the kernel, resulting in race conditions as described in
  the previous paragraph.  Especially for synthetic filesystems, it may be
  almost impossible for a wrapper to determine what a name will point to
  by the time it gets evaluated by the real kernel code.  With additional
  ABI's thrown into the mix, the complexity can get very high.

- Consistent policy expression issues.  Most system call wrapping tools
  have a hard time expressing consistent policies when the policies are
  highly complex.  Consider the following class of policies: there are two
  classes of processes, high integrity and low integrity.  Likewise, there
  are two classes of objects, high integrity and low integrity.  Permit
  low integrity processes to read from objects of either type, but write
  only to low integrity objects.  Permit high integrity processes to write
  to objects of either type, but read only from high integrity objects.
  This is a simple Biba integrity policy that uses information flow rules
  to protect high integrity subjects from interference by low integrity
  subjects.  This is very difficult to express via simple system call
  wrapping, because it's a fairly comprehensive policy regarding system
  objects -- files, sockets, other processes, etc.  And because it deals a
  lot with objects that are hidden behind weird abstractions, such as the
  fact that you access stream sockets using the same API calls as files,
  it requires the wrapping code to know a lot about the internals of the
  kernel.  System call wrapping can express simple policies relatively
  easily, but more comprehensive policies tend to run into problems.

Addressing some of these issues is where there's a lot of replicated code
in the LOMAC tree.  The TrustedBSD MAC tree attempts to address this by
providing tighter integration with the base kernel code, offering security
modules access to the life cycle of important kernel objects (vnodes,
credentials, sockets, mbufs, interfaces, mountpoints, ...), as well as
access control vectors so they can influence the access control decisions
of the kernel.  We plan to re-implement LOAMC using this framework to
reduce the level of code duplication while still remaining race-free.

This isn't to say that the systrace work isn't interesting, or even that
it should be ported.  It looks like a very interesting piece of work, but
it's important that we keep in mind increasing structural differences
between fine-grained kernels (such as 5.0-CURRENT) and more traditional
UNIX "giant lock" or SMP-free kernels.  Another important thing to keep in
mind is that in the OpenBSD world, kernel modules are not extensively used
to extend kernel functionality at run-time in production.  Wrapping tools
that rely on system call wrapping have a hard time dealing with run-time
expansion of the ABI.  So I would suggest someone port it over, and write
a cool paper on what they ran into, because there are probably a lot of
interesting problems.  And at the end of the day, it works really well, it
would be a great thing to add to our growing arsenol of security features.

Robert N M Watson             FreeBSD Core Team, TrustedBSD Projects
robert@fledge.watson.org      Network Associates Laboratories




To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.NEB.3.96L.1020601113616.86544F-100000>