The examples provided are some of the more complex cases that let you do advanced things.
You can shrink the amount of code required if you limit it to more simple cases as those shown in the slides.
For example, as derived from the OpenBSD presentation:
if (pledge("stdio fattr", NULL) == -1):
err(1, "pledge");
A similar (not completely equivalent, since OpenBSD chose some "interesting" definitions for their privileges, and is admittedly untested) example for Solaris might be:
priv_set_t *tmp = priv_str_to_set(
"PRIV_FILE_READ PRIV_FILE_WRITE PRIV_FILE_CHOWN_SELF",
" ", NULL);
/* Assert required privileges. */
if (setppriv(PRIV_ON, PRIV_PERMITTED, tmp) == -1)
err(1, "setppriv permitted");
if (setppriv(PRIV_ON, PRIV_EFFECTIVE, tmp) == -1)
err(1, "setppriv effective");
priv_inverse(tmp);
/* Drop all privileges not required. */
(void) setppriv(PRIV_OFF, PRIV_PERMITTED, tmp);
The big difference, I think, between the Solaris interfaces and the OpenBSD ones are that Solaris allows the process to temporarily drop privileges and then add them back, or permanently drop them. From the proposed OpenBSD interfaces, it looks they only allow the permanent drop model.
There are a few convenience wrappers that might simplify the above further, but the real point is not to compare efficiency of interfaces, but capability.
Also, Solaris offers the ability to restrict privileges of programs without source code modification (imagine a program you don't quite trust and don't have the source code to). I didn't see that in the OpenBSD presentation.
In their defense, they're also clearly still working on these interfaces, so there can't yet be a fair comparison. Solaris has had privilege interfaces for over a decade, so the model presented is a bit more mature obviously.
The only thing I'd mention is that Solaris tries to provide a default set of privileges that represent things closer to administrative boundaries, rather then implementation-specific ones, as implementation can change, but the basic high-level operations do not.
For example, Solaris has a file read/write privilege, but doesn't bother letting you restrict the ability to set file timestamps separately because that doesn't seem like a useful thing to do. It does however, provide separate privilege(s) for manipulating ownership of files, since that's clearly a different category of operations. OpenBSD currently seems to be focused on the implementation instead of the administrative-level operations being performed.
Thanks for the reply! I've got several responses, that are mostly unrelated to eachother.
1. "interesting definitions": As you note, "OpenBSD chose some 'interesting' definitions for their privileges". This was the core of my original thesis: because of the whole-system approach, they were free to choose "interesting" definitions that closely matched their current-implementation-specific usage patterns, making the whole thing more convenient, but less general. That's all my original post was trying to say.
2. code size: I see that conceptually these 2 code samples work similarly, but one has 5x more LOC.
3. dropping and picking up isn't really dropping: If I drop privileges, but can pick them back up, then if I decide to misbehave, picking them back up is just something I do first. Dropping them with the possibility of picking them up is security theater, not actual security.
4. sandboxing is addressed in other presentations: Earlier presentations explain why having tools to restrict the privileges of programs without source modification is inadequate; a common pattern for programs is that they require some privileges during start up, but then don't need them for the rest of execution; a source-unaware mechanism would have to allow the start-up privileges for the entire execution. Otherwise, it's not too different than existing priv-sep tools.
5. pledge() isn't about administrative privileges, it's about code vulnerability mitigation. Security tools around administrative boundaries are important, sure. But that is fundamentally not the problem that pledge() is trying to solve; pledge is /mitigating/ against vulnerabilities in the /implementation/ of the program. It's saying "for the remainder of my execution, I should only do these types of operations; if I try to do anything else, I have cracked & compromised."
2. code size: I see that conceptually these 2 code samples work similarly, but one has 5x more LOC.
I think that's nit-picking a bit, especially since, as I said, there are some other convenience functions that could be used depending on the situation. But regardless, it's hardly onerous. We're talking about 7 lines of actual code vs. 2 lines. That's not even worth arguing about, especially as most applications do this once if they're permanently dropping privileges.
3. dropping and picking up isn't really dropping: If I drop privileges, but can pick them back up, then if I decide to misbehave, picking them back up is just something I do first. Dropping them with the possibility of picking them up is security theater, not actual security.
No, it's actually not security theatre at all. I think you misunderstand the threat model that's trying to be addressed. The kernel is enforcing the restrictions.
Note that I also specifically said Solaris allows both -- the developer can temporarily drop privileges or can permanently drop them or use a combination thereof. Each style of interface is appropriate for a different situation.
For example, consider a case where your program allows the execution of a user script to retrieve a password required to unlock an SSL Private Key file (Apache does this). For the duration of that operation, you can set your effective privileges to be very limited, and thus any programs you fork/exec can also inherit those very limited privileges and you have additional mitigations against certain attacks.
Or consider any other scenario, where during that particular period of execution, you may have to accept untrusted user input. By limiting your effective privileges during that operation, you can significantly mitigate potential attacks against your application.
Privilege bracketing (as this is called) allows you to carefully control sensitive information to ensure that it is not compromised. It's important because sometimes programs do need a higher level of privilege, but only for short durations of program execution. Privilege bracketing is not always the correct answer for an application, but sometimes it is best and only practical choice.
The key benefit of privilege bracketing is to narrow the window of a program's vulnerability so that it is as small as possible, reducing the damage any exploit can do. For example, in order for a process to be able to write to a file, it is only necessary for that file to be opened for write access. In other words, we would assert privileges only for the open() call (relinquishing them immediately after); they should not be asserted for the write() call.
4. sandboxing is addressed in other presentations: Earlier presentations explain why having tools to restrict the privileges of programs without source modification is inadequate; a common pattern for programs is that they require some privileges during start up, but then don't need them for the rest of execution; a source-unaware mechanism would have to allow the start-up privileges for the entire execution. Otherwise, it's not too different than existing priv-sep tools.
Yes, sandboxing is not a complete answer, but if you don't have the source code to an application (which happens often in an enterprise environment), it is one of the best ways to ensure that an application behaves as expected.
As for handling applications that only need certain privileges for startup, Solaris provides a mitigation for that via SMF (Service Management Facility). In particular, it's possible to have SMF allow an extended set of privileges to start the service, and after the service is online, then modify the effective set for the program. That may not work for all programs, but it's still important.
As a historical example, you could modify Apache to start with a greater set of privileges than needed after startup:
5. pledge() isn't about administrative privileges, it's about code vulnerability mitigation. Security tools around administrative boundaries are important, sure. But that is fundamentally not the problem that pledge() is trying to solve; pledge is /mitigating/ against vulnerabilities in the /implementation/ of the program. It's saying "for the remainder of my execution, I should only do these types of operations; if I try to do anything else, I have cracked & compromised."
Fundamentally, operations a program can perform is an administrative consideration - not just an implementation consideration. For example, consider a libc function that historically used a specific syscall, but is later optimised to no longer require a certain syscall (as the OpenBSD folks themselves point out as examples of things they are changing). If you represent the privilege in terms of the syscall, then when the implementation changes, either the program breaks or now the program can perform an operation you didn't previously want to allow.
Not only that, a privilege capability model needs to be represented in terms not only appropriate for a developer, but for an administrator as well. Especially since administrators will often require the ability to restrict the capabilities of programs for which they have no source access.
Solaris attempts to strike a balance between the two as you can see in the list of privileges here:
Also, this is precisely why both the ability to permanently and temporarily drop privileges is important. As an example, a program at startup could drop everything but the ability to read and write files permanently. Later, it could then further use privilege bracketing to temporarily drop the ability to read or write files (as appropriate) during certain parts of program execution.
In short, I think it's important to not limit perspective of privilege capability to a model where you treat the entire program as hostile -- privileges can be used not only by the administrator to ensure a program behaves as expected, but by the application itself to insulate itself from other bad actors.
You can shrink the amount of code required if you limit it to more simple cases as those shown in the slides.
For example, as derived from the OpenBSD presentation:
A similar (not completely equivalent, since OpenBSD chose some "interesting" definitions for their privileges, and is admittedly untested) example for Solaris might be: The big difference, I think, between the Solaris interfaces and the OpenBSD ones are that Solaris allows the process to temporarily drop privileges and then add them back, or permanently drop them. From the proposed OpenBSD interfaces, it looks they only allow the permanent drop model.There are a few convenience wrappers that might simplify the above further, but the real point is not to compare efficiency of interfaces, but capability.
Also, Solaris offers the ability to restrict privileges of programs without source code modification (imagine a program you don't quite trust and don't have the source code to). I didn't see that in the OpenBSD presentation.
In their defense, they're also clearly still working on these interfaces, so there can't yet be a fair comparison. Solaris has had privilege interfaces for over a decade, so the model presented is a bit more mature obviously.
The only thing I'd mention is that Solaris tries to provide a default set of privileges that represent things closer to administrative boundaries, rather then implementation-specific ones, as implementation can change, but the basic high-level operations do not.
For example, Solaris has a file read/write privilege, but doesn't bother letting you restrict the ability to set file timestamps separately because that doesn't seem like a useful thing to do. It does however, provide separate privilege(s) for manipulating ownership of files, since that's clearly a different category of operations. OpenBSD currently seems to be focused on the implementation instead of the administrative-level operations being performed.