Hacker News new | past | comments | ask | show | jobs | submit login
Using tame() in userland (marc.info)
66 points by protomyth on Aug 28, 2015 | hide | past | favorite | 23 comments



For those wondering what this is about: http://marc.info/?l=openbsd-tech&m=143725996614627&w=2

     The current process is forced into a restricted-service operating mode.
     A few subsets are available, roughly described as computation, memory
     management, read-write operations on file descriptors, opening of files,
     networking.  In general, these modes were selected by studying the
     operation of many programs using libc and other such interfaces.
Sounds pretty interesting.


It seems really interesting, and a good idea.

What I don't understand though is the incentive, what motivates a program-author to "[b]e careful writing such diffs; you need to fully understand the program and handle all cases"?


In addition to the other fine replies, I'd observe this falls in the set of things that are pretty easy to use if you start with them from scratch, but are hard to retrofit on to existing code. To retrofit requires the mentioned extensive knowledge of the target codebase... to use it on a new codebase just requires you to do any sort of testing at all, note the new thing you wrote is a security violation, then think about what that means and what you should do about it at precisely the point in time you have all the relevant context loaded in your brain.

At the moment the motivation is to armor existing code, but the general case for tame across its entire lifespan is to be used in new code. In that case the motivation is the same as any other security practice... not to see your code be fingered as the vulnerability that let $BAD_THING happen.


i think the recent hiding of unused libc symbols is related to the tame effort.


No, entirely unrelated.


If there is somehow an exploitable vulnerability in the program, if exploited after the tame call the attacker only takes control of a restricted process, which hopefully mitigate or blocks the attack.

This a very smart application of the least privilege principle. The idea is that most programs don't need most syscalls after they complete their initialization, and thus dropping the privilege to use them is both cheap, safe and effective.


Presumably the program author (or OpenBSD ports maintainer) knows more about what permissions the program requires than the end-user sysadmin who would be configuring SELinux, AppArmor, etc.


It depends, really. In terms of which system calls the program needs, that's certainly true.

However, in terms of things like "which files will this program need to access", the system administrator is in a much better position, because these kinds of questions often depend on how the program is used in a particular environment.

The approaches are complementary.


I'd imagine that it would be easy to miss a disallowed system call down one rarely used path in a larger program.

It might be interesting adding the ability to do something like:

    #define TAME_ENABLE
    #define TAME_ENABLE_FOO
    #define TAME_ENABLE_BAR
    #include <foo.h>
which would cause a compile error by removing declarations for uncallable functions.


>Generally there are two models of operation. The first model requires a major rewrite of application software for effective use (ie. capsicum). The other model in common use lacks granularity, and allows or denies an operation throughout the entire lifetime of a process. As a result, they lack differentiation between program "initialization" versus "main servicing loop". systrace had the same problem. My observation is that programs need a large variety of calls during initialization, but few in their main loops.

>Some BPF-style approaches have showed up. So you need to write a program to observe your program, to keep things secure? That is insane.

>So I asked myself if I could invent a simple system call, which people would place directly into programs, between initialization and main-loop.

>Subsequent calls to tame() can reduce abilities further, but abilities can never be regained.

What you are suggesting is not compatible with tame.


What I'm suggesting is entirely compatible with tame. It doesn't change anything about the implementation, but prevents you from accidentally calling functions that would error out.

The only thing that my suggestion optionally changes is that using, eg, socket() would be a compile time error AND a runtime error, instead of only a runtime error, if you tell the system that you want it. Don't define the macros to disable functions that you are trying to make uncallable? Your code is 100% unaffected.


It's not the same because the restrictions aren't in effect at the start of the program until the tame call is made.


Again, this doesn't change anything on that front. You just don't define the declaration hiding macros in the files that do the init.

It just prevents random-seeming issues off of the common path due to calling a function that you shouldn't have called.


Reading that man page sounds like the way to go about it is start with `tame(0)` as early as possible in the program and see how it fails. then iterate pushing `tame()` down and expanding the flags until the program functions.


There some good previous HN threads discussing the earlier announcements such as https://news.ycombinator.com/item?id=9928221


  --- bin/echo/echo.c	14 Dec 2014 16:55:59 -0000	1.8
  +++ bin/echo/echo.c	26 Aug 2015 22:07:37 -0000
  @@ -30,6 +30,8 @@
    * SUCH DAMAGE.
    */
   
  +#include <sys/tame.h>
MUCH TAME!


Hooray simple security hacks!

....and booooo simple security hacks.

In the previous thread, someone says "I really like this solution. Far better than pissing around with SELinux configuration and it's well contained." Which is a little bit like saying, "I really like these airbags. Way easier than using a seatbelt."


Only if the seat belt consists of thousands of strands you have to piece together, and if you screw up one strand your entire belt could fail. I've hung myself with SELinix a few times as complexity grows even just trying to set a policy(s) for an Android phone with half the features removed. Even the NSA guys on the SEAndroid mailing list have been stumped sometimes with complexity errors.


It's actually a consistent mechanism for doing privilege dropping on a whitelist level. The same cannot be said for an API like POSIX capabilities.


My initial reaction was that it didn't match my use. It doesn't seem possible to do what my use would require, which would be things like dropping specific identified privileges without affecting what other privileges the process might currently have. This would need some way for reading the process's current restrictions. The current API is write-only.

Moreover none of the privilege sets seemed to allow the possibility of chain loading once privileges had been dropped, as none of them allow execve(). OpenBSD tame() is, as its blurb states, not designed with the model in mind of programs where initialization parts (consider UCSPI servers, for example) are in a separate executable that then chain loads through common privilege dropping tools to the main part of the program.

It's not as easy to wrap tame() into some shell-level composable tool as it is to wrap jail() into the jexec tool.


Apart from SELinux is not available in OpenBSD, so you can't use both in tandem.


Would that work for tools like busybox? I guess it'll work as long as it's set only once, but doesn't busybox has a mode where one command can call internally another, and then it'll have to set tame() again, or maybe even restore it?


The docs say you'll get EPERM for trying to increase permissions, so I'd guess there's no way to restore it.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: