![]() |
|
OpenBSD Security Functionally paranoid! |
![]() |
|
Thread Tools | Display Modes |
|
||||
![]()
Preface
For those unfamiliar with it, pledge(2) is a system call (a "syscall") that a programmer can use to limit her program to specific subsets of future syscalls. It's a relatively new risk mitigation feature for OpenBSD, initiated by applications. Most of the userland applications in OpenBSD have had pledge() added for 5.9. Some key ports in the ports tree have also recently added pledge() protections, such as the www/chromium browser, as discussed recently in the OpenBSD Journal. The Story A list of ports that should use pledge() was posted to the ports@ mailing list this past weekend. One of those appplications was archivers/p7zip, a portable version of the Windows 7zip program. I maintain the OpenBSD port. The application is 80+K lines of undocumented code, spread across more than 200 source code modules. And I'm not a programmer. IT guy? Sure.I've been away from programming for at least 25 years, and only recently taught myself the rudiments of C and C++. I can read source code, but some things I must still refer to documentation to understand, and when crafting my own programs, I often confuse when I need pointers to pointers or just pointers, or whether I should reference or dereference. While I maintain this port, I have little understanding of this application's inner workings. But the culture of the project is very clearly, "Don't ask for something to be developed for you. Develop it yourself. If you can't do it, at least try. If you fail when trying, you'll know what knowledge you need to acquire or what help you need to request." So I decided I must try. I began with an empty string in a pledge() promise near the beginning of the main process function, and built the application with debugging enabled and optimization off. And started looking through stack traces, adding categories of syscalls to the promise string in my pledge() as I progressed, then reviewing the next stack trace with each iteration. And when I saw code paths diverge, I learned I needed to add another pledge point. It was easy. The stack traces in the core files let me see the program flow with each change. I posted the patch to the ports@ mailing list. First when I hadn't yet determined what to do about the code path divergence, then a revised patch after I'd figured it out. I received an Email today confirming the patch is in review and test. It appears (to quote the developer) "sane" and it is being more fully tested to see if it can be broken. If not, it will be committed. Summary My total time invested in this? Six hours. For a very large, complicated, undocumented application, by a self-described non-programmer. Key tools used :
The pledge() tool is easy to use. If I can use it, so can you. ![]() --- Here's are links to Theo de Raadt's slides and video on pledge() from Hackfest 2015. Last edited by jggimi; 19th January 2016 at 07:33 PM. Reason: typo, clarity |
|
||||
![]()
Here's the excerpt from my post to to ports@ containing the new patch file. Since this is a new file intended to be added with patch(1), all lines in the file are inserts and begin with a + character. Only the lines that begin with ++ are actually inserted into the application. Those merely add two pledge() calls at different points in the main module, wrapped in pre-processor token tests -- this application builds the same module multiple ways for different executable binaries.
They are also nearly the only documented code fragments in the application. ![]() Code:
Index: patches/patch-CPP_7zip_UI_Console_Main_cpp =================================================================== RCS file: patches/patch-CPP_7zip_UI_Console_Main_cpp diff -N patches/patch-CPP_7zip_UI_Console_Main_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-CPP_7zip_UI_Console_Main_cpp 18 Jan 2016 15:05:44 -0000 @@ -0,0 +1,43 @@ +$OpenBSD$ + +Pledge archivers/p7zip binaries + +--- CPP/7zip/UI/Console/Main.cpp.orig Sat Oct 17 11:20:22 2015 ++++ CPP/7zip/UI/Console/Main.cpp Mon Jan 18 10:05:31 2016 +@@ -484,6 +484,18 @@ int Main2( + #endif + ) + { ++ ++// pledge 7za and 7zr at this point, they take different paths than 7z. ++ ++#ifndef EXTERNAL_CODECS ++ ++ if (pledge("stdio rpath wpath cpath fattr", NULL) == -1) { ++ perror("pledge"); ++ exit(1); ++ } ++ ++#endif ++ + #if defined(_WIN32) && !defined(UNDER_CE) + SetFileApisToOEM(); + #endif +@@ -579,6 +591,17 @@ int Main2( + codecs->CaseSensitiveChange = options.CaseSensitiveChange; + codecs->CaseSensitive = options.CaseSensitive; + ThrowException_if_Error(codecs->Load()); ++ ++// pledge 7z here ++ ++#ifdef EXTERNAL_CODECS ++ ++ if (pledge("stdio rpath wpath cpath fattr", NULL) == -1) { ++ perror("pledge"); ++ exit(1); ++ } ++ ++#endif + + bool isExtractGroupCommand = options.Command.IsFromExtractGroup(); + |
|
||||
![]()
An introduction to pledge(), for those who are not familiar with it.
One thing you can do In Capsicum, but not with pledge() as far as I can see, is stuff such as (from capsicum(4)): Code:
Limit operations that can be called on file descriptors. For example, a file descriptor returned by open(2) may be refined using cap_rights_limit(2) so that only read(2) and write(2) can be called, but not fchmod(2). The complete list of the capabil- ity rights can be found in the rights(4) manual page. Not sure how well this works in the real world... In the linked slides it's claimed that Capsicum is too complicated. But on the other hand, pledge() seems a bit too simple in its current incarnation...
__________________
UNIX was not designed to stop you from doing stupid things, because that would also stop you from doing clever things. |
|
|||
![]()
I am not expert on pledge and chroot in OpenBSD, so don't get me too serious on this.
If I understand correctly enough, for me one thing missing is ability to restrict processes also after fork() AND execve(). I think it could be used to build sandbox environment using chroot(). Create chroot, then drop privileges inside chroot (became another user), then pledge not using system calls referring to login as another user (especially root) and after that start Firefox. I think it could be quite difficult for attacker to go out of this chroot, read files outside chroot and send them through Internet. But maybe I am wrong on something. Add2: I understand that for some programs it is not fitting well for example shell i.e. ksh should be able to pledge itself tightly and also processes executed by ksh should not be pledged by default at all. On the other hand maybe there is a place for second kind of promise exec to also pledge other executed programs (fork() AND execve()). But maybe there are more difficulties than I see. Last edited by e1-531g; 23rd January 2016 at 01:37 PM. Reason: Added second part of post |
|
||||
![]()
It is not a chroot(). Its restrictions only impact the calling process.
Even though the currently unimplemented whitelist parameter will further restrict the capability of *path promises, if a new application is started by the pledged application, the new process is started without pledge() restrictions. See the exec promise in the pledge(2) man page. |
|
|||
![]()
@jggimi
I understand that. I must admit that I do not discussed yours patch but rather my view of pledge() in general. I have even wrote small program in C (not good style, so I would rather not post source code) to see if I just fork() without execve() pledge would kill my process and yes, it kills process. I have also added few words to previous post in parallel to you writing yours post. |
|
||||
![]() ![]() |
|
||||
![]()
I had an offer of assistance from a developer today, so I took it. He'd noted a CVE that had not been addressed by upstream, and he spent some time devising pledge() call points to revoke privileges for functions that do not write to disk.
I adopted Debian's mitigation for the CVE, added the debugging tokens I'd mentioned planning above, and the two of us spent a good deal of time testing the secondary pledge() calls today. The included test suite is only a partial test of all capabilities, and so we were doing lots of manual testing also. One thing uncovered was a missing pledge() of self-extracting archives. Not very useful on OpenBSD, but certainly possible. Specifics and patches were just posted to ports@. Last edited by jggimi; 25th January 2016 at 03:48 AM. Reason: I can't post without typos |
![]() |
Thread Tools | |
Display Modes | |
|
|
![]() |
||||
Thread | Thread Starter | Forum | Replies | Last Post |
Share your BSD story for the BSD Now Holiday show | ibara | News | 1 | 14th December 2014 06:22 PM |
Migrating from iptables to pf, a love story | Popelicious | OpenBSD Security | 7 | 19th April 2013 08:46 AM |
OpenBSD A Puffy in the corporate aquarium [success story] | vermaden | News | 2 | 22nd April 2011 01:08 AM |
Learning Programming | Crypt | Programming | 35 | 27th October 2008 04:54 PM |
Learning Perl | mtx | Book reviews | 7 | 22nd October 2008 05:55 PM |