
A subsystem to restrict programs into a “reduced feature operating model” - cnst
https://marc.info/?l=openbsd-tech&m=143725996614627&w=2
======
geofft
This doesn't seem particularly better than Linux's seccomp-bpf or OS X's
seatbelt. In particular, I don't really understand the complaint about not
wanting to write a program, then turning around and writing a _system call_ ,
running with full privilege on the system, that hardcodes all sorts of things
about userspace.

I wish he'd acknowledge and discuss prior, effective work in this space
instead of saying things "showed up" and they're "insane". For instance, a
direct comparison to either seatbelt or seccomp-bpf would make it clear that
the distinction between initialization vs. steady state is well-explored in
production systems using this (like sandboxed Chrome renderers) and not novel.

~~~
comex
The implementation is better than OS X, because OS X has a _ton_ of kernel API
surface available to userland and much of it can't even be sandboxed, while
tame starts with one syscall and works its way up.

I agree it's not better than seccomp-bpf plus a library to supply these kinds
of common policies in userland, with the caveat that I don't know of any such
library in common use on Linux.

~~~
geofft
Yeah, seatbelt is not that compelling in practice, but that's true of the OS X
kernel/userspace boundary in general. :/ But the design of it seems to match
what's wanted here, exactly: you can enable restrictions during the running of
a process, and OS X ships with a handful of standard seatbelt profiles.

pcwalton's gaol, which has both seccomp-bpf and seatbelt backends, has a
concept of "profiles," which I think matches the general idea here:

[https://github.com/pcwalton/gaol](https://github.com/pcwalton/gaol)

To be fair I wouldn't use it in production yet, but it's quite a bit more
reviewed than tame(2) is right now.

------
TheLoneWolfling
So, do I have it right that this is effectively a way of a program being able
to declare to the operating system "I shouldn't ever do <x>"?

Because, if so, that makes a whole lot of sense. (Adding security "for free"
generally does).

This could conflict with on-the-fly upgrades, though. If it turns out that
some later version of your program does in fact require <x>, then you'll have
to kill and restart the process as opposed to upgrading on-the-fly. Perhaps
not the end of the world, but worth noting.

~~~
adrusi
It can also be used by a parent process to limit the functionality of a child
process, by calling tame() after fork() and before exec(). I can imagine this
being used with some a "tame" command in the shell to run untrusted programs.
Of course I don't know how thorough the sandboxing is, and I wouldn't trust it
to make unsafe programs completely safe.

~~~
Athas
> I can imagine this being used with some a "tame" command in the shell to run
> untrusted programs.

This might not be useful in practice. The inspiration behind tame() is the
observation that a program usually needs many more rights at initialisation-
time than in its main-loop. Thus, tame()ing a program before it even starts is
unlikely to be practical - if you give it enough permissions to successfully
start (including whatever is needed for dynamic linking), you may not have
reduced its capabilities much.

------
ghshephard
This is pretty brilliant/obvious in hindsight. In addition to the sandboxing
protection, you also have a really good inventory of what privileges the
application requires. Looking over the diffs in the applications - most of
them are two or three lines - a #include <sys/tame.h> followed by something
simple like tame(TAME_STDIO | TAME_DNS | TAME_INET);

What I really like about a lot of the OpenBSD initiatives, is they don't
overthink their solutions - they make them as simple as possible, but no
simpler. Signify, which avoided the entire web-of-trust/PKI complication is
another example.

~~~
evincarofautumn
Yeah, it’s a basic dynamic role/coeffect system, which goes a long way toward
enforcing correctness and safety, much as types do (whether static or
dynamic).

------
Animats
Having fine-grained capabilities and the ability to turn them off is always
useful. The usual problem is that some component needs to, say, open a file,
so all code gets "open file" privileges.

There's a tool like this for Android phones. It not only can turn privileges
off for an application, but also offers the option to provide apps fake info
for things they don't need. You can, for example, deny address book access; if
the app tries to access the address book, it gets a fake empty one. You can
deny camera access; the app gets some canned image. This allows you to run
overreaching apps while keeping them from overreaching.

~~~
raverbashing
Yeah, for 'open file' kind of stuff it would be better to have a real sandbox
(I think Windows began doing something like this, not sure if in Vista or 7,
that if programs wanted to write to certain restricted places they can - but
this is written to their sandbox, so if they read it later they can get the
files but with no effect on the system files)

~~~
TazeTSchnitzel
Vista did that as a compatibility workaround. Older Windows apps from the 9x
and XP era were used to being run as Administrator and being able to write
directly to Program Files and the like. In order to make some of these work on
Vista without running them as Administrator, the solution was to lie to the
applications and make their writes go through to sandboxes instead.

The sandboxing was flawed, though. It causes problems for some applications.
For example, Gang Garrison 2, a game I have worked on, fails to update itself
if stuck in Program Files and not run as admin.

------
vezzy-fnord
For another example of a similarly beautiful interface that echoes "difficult
solution made stupidly simple to use", checkpointing under DragonFly BSD:
[http://leaf.dragonflybsd.org/cgi/web-
man?command=sys_checkpo...](http://leaf.dragonflybsd.org/cgi/web-
man?command=sys_checkpoint&section=2)

On an unrelated note, I've always had respect for how Theo de Raadt is both
the project leader of a complete BSD system, yet also an active hacker.
Contrast to Linus Torvalds, who's mostly a manager nowadays.

~~~
TheLoneWolfling
On an unrelated note, but tangential to your link, why can I not save and
restore processes? It seems like something that would be relatively easy to do
- suspend all threads, save the thread control blocks, mark all pages as
fault-on-write, resume threads, and start saving pages, unmarking them once
they are saved. If/when a thread faults, copy (or save immediately) that page
and then resume that thread. (Or potentially don't resume the threads until
after you save everything, although that may cause problems...) You need to
deal with file handles / etc, but that can be done too.

Not really different than hibernation.

~~~
thorwafspp
You've tickled on the problem but not quite nailed it.

> You need to deal with file handles / etc, but that can be done too.

That's actually the hard part. To get a real image of that process in time,
you need to snapshot the full filesystem state, too. Or it could change out
from beneath your program. Even more complicated: network state.

~~~
Demiurge
Why is network more complicated? I would think network doesn't have any
atomic/uninterruptible states filesystem might?

~~~
MBCook
It's easy to re-open a file (assuming it's still there), but with sockets your
IP may have changed, the remote IP may have changed (which you may have stored
in your working memory that got checkpointed), DNS may point you to a
different service entirely, you could have had to do some kind of port
knocking or something to get that connection open in the first place.

I know this kind of stuff is being worked on so VMs/containers/namespaces can
be moved around but it seems to be one of those things that gets really
complicated when you try to do it transparently for userspace.

~~~
Demiurge
IPs, DNS settings change on running programs all the time, that doesn't seem
as unusual as re-opening a file that's actually not there. A unix socket is an
interesting mixed case :)

------
binarycrusader
See also, Solaris' Role-Based Access Control and Privileges models.

Privileges (seems to fit the post):
[https://blogs.oracle.com/casper/entry/solaris_privileges](https://blogs.oracle.com/casper/entry/solaris_privileges)

[http://www.c0t0d0s0.org/archives/4075-Less-known-Solaris-
fea...](http://www.c0t0d0s0.org/archives/4075-Less-known-Solaris-features-
RBAC-and-Privileges-Part-3-Privileges.html)

Programming with Privileges Example:
[http://docs.oracle.com/cd/E23824_01/html/819-2145/ch3priv-25...](http://docs.oracle.com/cd/E23824_01/html/819-2145/ch3priv-25082.html#priv-12)

Overview: [http://www.c0t0d0s0.org/archives/4077-Less-known-Solaris-
fea...](http://www.c0t0d0s0.org/archives/4077-Less-known-Solaris-features-
RBAC-and-Privileges.html)

In particular, the Solaris privileges model allows a program to gracefully
degrade functionality and drop and reinstate privileges at different points of
execution.

------
Mister_Snuggles
This is an interesting idea, but something about having various "behaviours"
baked into the logic concerns me. I'm certain that Theo has thought about this
and understands the implications better than I do though.

For example, if your process has TAME_GETPW opening /var/run/ypbind.lock
enables TAME_INET. The reasoning behind this makes sense, but now it means
that yp always has to open that file before it can do its thing. The behaviour
of yp always opening that file before accessing the network is now required by
the kernel.

The saving grace is that OpenBSD (and the other BSDs) are developed as a
unified system, so if yp ever changes to no longer use that file, that change
will only come as part of a version upgrade that includes the kernel, etc.

------
icebraining
Windows 8 has an equivalent of this, using a "mitigation policy" called
ProcessSystemCallDisablePolicy, which is set using
SetProcessMitigationPolicy().

Chrome uses this for their sandbox of rendering processes.

~~~
gpvos
It looks like you can only disable GUI calls with it (the very sparse
documentation seems to contradict itself a bit though). Also, a model in which
you specify which calls you want to enable (and everything else gets disabled)
is stronger than one in which you specify which ones you want to disable.

~~~
higherpurpose
Indeed, it's the whole whitelist vs blacklist [1] debate. Whitelists are
radically safer.

[1] -
[https://farm9.staticflickr.com/8669/16418068728_b8dd8aa200_c...](https://farm9.staticflickr.com/8669/16418068728_b8dd8aa200_c.jpg)

------
nitrogen
I recently asked for exactly this on StackOverflow, but for Linux[0]. Is
anyone aware of an interface to seccomp-bpf on Linux that is as easy to use as
this tame() syscall?

If not, does anyone want to join forces to create one? An ultra-simple library
that provides tame()-like functionality on all capable platforms should make
writing secure software a lot easier.

[0] [https://stackoverflow.com/questions/31373203/drop-
privileges...](https://stackoverflow.com/questions/31373203/drop-privileges-
as-regular-non-root-user-for-sandboxing/31373324#31373324) if anyone's curious

~~~
vidarh
I would look into the Chrome/Chromium sandbox code, as they seem to have at
least some facility for parsing simple profiles.

This page has some details:

[http://www.chromium.org/chromium-os/developer-
guide/chromium...](http://www.chromium.org/chromium-os/developer-
guide/chromium-os-sandboxing)

------
brynet
The kernel pieces of tame(2) have just been committed:

[http://marc.info/?l=openbsd-
cvs&m=143727335416513&w=2](http://marc.info/?l=openbsd-
cvs&m=143727335416513&w=2)

------
philsnow
I can't see where in this patch forked children inherit the `ps_tame` from the
parent process. I don't know this kernel at all but it seems like something
like this should be in sys/kern/kern_fork.c

    
    
        pr->ps_tame = parent->ps_tame
    

Otherwise tamed processes could just fork a process to do things they've
declared that they won't do.

~~~
brynet
A tamed process cannot even call fork(2) unless the _TAME_PROC_ flag is
passed, in which case I'd assume the child process would then inherit a copy
from the parent as part of a normal fork(2) operation. It can be later revoked
by the child, but it may want to keep it for kill(2).

~~~
philsnow
I'm `cvs get`ting as fast as I can to read the rest of sys_fork.c to sate my
curiosity, but CVS incredibly slow. I'm spoiled by how git packs the repo.

~~~
philsnow
turned out to be way faster just to grab
[http://mirrors.sonic.net/pub/OpenBSD/5.7/sys.tar.gz](http://mirrors.sonic.net/pub/OpenBSD/5.7/sys.tar.gz)
, if anybody else wants to poke around and doesn't want to wait for cvs.

~~~
glass-
You can also view the source on the cvs web mirror:
[http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/](http://cvsweb.openbsd.org/cgi-
bin/cvsweb/src/)

------
wfunction
This won't work for programs that allow for plugins, which are arguably those
that need the most protection. Programs don't generally know what permissions
plugins when they are compiled.

~~~
MBCook
Could you provide examples?

The stuff I'm thinking of that would plugin to Firefox or Photoshop probably
do things that would already be allowed (read/write files, allocate memory,
access network).

Either way this seems like an extremely simple way to lock down all the little
command line utilities and small programs that make up a working unix system,
so that if someone does get arbitrary command execution by other programs it
gets much more difficult to chain exploits.

~~~
simcop2387
think more like emacs or vim. plugins can add any number of things that isnt
just file read/write. they can add new syscalls for a built in debugger
(ptrace, strace, etc.) or even add an opengl layer for coding in 3d.

~~~
wfunction
That's basically what I meant, thanks for explaining it clearly.

------
SloopJon
This interface makes a system call at run time to reduce privileges. I wonder
if there is a way to do this statically and automatically, either with some
header file magic, or by analyzing the symbols in the executable: just assume
at link time that any system calls it makes (and only those) are allowed.

~~~
Mordak
Looking at a program statically and limiting its privileges automatically
would be good (autotame?), but it misses the point that tame appears to be
trying to solve. Specifically, it is often the case that programs need more
privilege once at initialization time, and less privileges later. So at
startup, your program needs to open some files, and open listening network
sockets, etc., but once initialization is done it just needs CPU. A static
analysis would see that the program opens files and listening sockets and
stuff, and grant those privileges forever. If you want to capture the reduced
privileges required after the one-time initialization then you need to modify
the program to use something like tame (or capsicum, etc.).

------
cnst
For comparison, the man-page for FreeBSD's capsicum API:
[http://mdoc.su/f/capsicum.4](http://mdoc.su/f/capsicum.4).

------
stock_toaster
Great to see another attempt at this model. I do like capsicum (seems pretty
straightforward), but it seems like it can require some added complexity with
things like casperd for dns.

Very interested to see how this works out.

------
SFjulie1
Let's say I received a SIGHUP and I need to reload my configuration after I
dropped my right for reading /etc/ and all the syscall for provisioning my
resources.

How screwed am I?

~~~
brynet
You would have a parent process that retains permissions to read configuration
and sends changes to children, i.e: imsg_*(3) on OpenBSD.

[http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-
current/man3/...](http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-
current/man3/ibuf_add.3)

------
josephcooney
This sounds very similar to code access security (CAS) that Microsoft's CLR
had from around 2000 (but at the OS level rather than the VM level).

------
vmorgulis
It reminds me SELinux.

~~~
Taranis
Not really.

------
MichaelCrawford
A simple easy way to keep a lid on privelige escalation is to remove all the
files that you computer does not absolutely require to do its job.

Especially the development tools: the Morris worm enabled portability by
distributing itself in source code form then building its binary on its target
hosts.

My sister once read a novel about some very traditional, strictly religious
people who fastened their shirts with string ties as they felt buttons were
hooks that the Devil could use to grab hold of you.

I feel much the same way about files. I dont know what tomorrow's zero-day
will look like but the chances are quite good that it will depend on a file
that is installed by default. Cliff Stoll wrote in "The Cuckoo's Egg" of a
subtle bug in a subprogram used by GNU emacs for email. Had the Lawrence
Berkeley Laboratory used vi rather than emacs they would not have been
vulnerable. ;-D

Yes it is a step in the right direction not to run daemons or windows services
you dont need but its even better to remove them.

In 1990 I wrote an A/UX 2.0 remote root exploit to drive home my objection to
one single file having incorrect permissions. Its source was about a dozen
lines. That particular file was required but our default installs have many
files we dont really need.

Also if you can read - not just execute - the binary to any program or library
then your malware can load it into its memory then execute it. We have no way
of knowing who is going to do that tomorrow but we do know there are many
binaroies we do not really need.

If you develop code for your server, install the same distro in a vm on your
desktop box then compile it there.

~~~
gnuvince
> A simple easy way to keep a lid on privelige escalation is to remove all the
> files that you computer does not absolutely require to do its job.

Taken to its logical conclusion, you sort of end up with a unikernel system,
like Mirage OS[1]: only the code necessary for the execution of the service is
compiled into the kernel. These systems don't even have a shell.

[1] [https://mirage.io](https://mirage.io)

~~~
MichaelCrawford
Many embedded systems are that way.

While it is helpful that hardware memory management will protect against
erroneous and malicious code, even better is for the code to be correct and
ethical.

This because the MMU hardware takes up power, it costs money, generates heat
and uses real estate. Also the software is complex and uses a lot of memory
for page tables and complex allocation schemes.

The Oxford Semiconductor 911, 912 and 922 didnt even have a kernel nor did
they have dynamic memory allocation, just stack and static memory with an
infinite loop operating a state machine. A huge PITA to debug but the memory
and flash were quite cheap because there werent very much of either.

------
higherpurpose
So when is OpenBSD getting rewritten in Rust?

