I find Linux’s approach on capabilities underwhelming, and not even close to a true capability-based system.
For example, you can pass a program a capability to bind any privileged port, but not a specific one. For this scenario, just passing an fd bound to the port is actually much simpler and safer. For other capabilities, they’re just too coarse.
The fact that capabilities are implicitly inherited also doesn’t sound like a good approach on security. It’s likely like this due to backward compatibility, but I really think that capabilities ought to be passed explicitly, and we should be able to transfer them between processes. In fact, using an fd as a handle for capabilities would probably be a much clearer and explicit approach.
This is misunderstanding what the feature is for. The point wasn't to architect a "capabilities-based system" from scratch (see LSM/selinux/apparmor for work in that space). It was to split up the very practical set of "things setuid is traditionally used for" into finer chunks.
The setuid binaries already existed, and this was a means to making them (much) more secure without API changes.
The use of a fd for access to a file is one of the places where Linux follows a capability model. Not coincidentally, this is a part of the Linux API which is has not proven a good target for attacks.
I feel like it was around 10 to 15 years ago that some people were excited to eliminate all the setuid on various binaries in a linux distro, by using linux file xattr capabilities. And eventually it became apparent that this wasn't such a great scheme; many capabilities can be used to escalate to full root, basically. So, yeah, I think this is known. It takes some effort to find some good write-ups from back then, but here we go: https://lwn.net/Articles/632520/https://forums.grsecurity.net/viewtopic.php?f=7&t=2522&sid=c...
If they're not passed around as objects a la FILE*/fd they're not even really capabilities, just (sparkling) fine-grained ambient authority (which still has value to be clear).
A shared global namespace ultimately makes it very difficult to have a decent capability based security system. Namespaces limited to the set of actions you have and a hierarchy of capabilities whereby children can only be given access to capabilities their parents have is required for a sane view of how things work. Much like encapsulation makes it easier to reason about abstractions in a program, this nested hierarchy of capabilities makes it easier to reason about the privilege of various parts of the system. Instead we have soup where no one can quite reason about what has access to what.
Note it is just a set of flags that subdivide the privileges root has which is potentially an improvement over what we had before but it's nothing like the real capability-based security
that you had in AS/400 or the iAPX 432 where a "capability" is a reference to a system object with associated privileges. It is possible to get this into a POSIX-like system
> whereby children can only be given access to capabilities their parents have
The one thing that makes capabilities usable is that they don't need to follow that rule.
If you don't have processes that let your programs get capabilities from any source other than their creation, you are better just adding your program names into your ACLs.
I don't think you can bolt something like what you're describing onto an existing kernel (like linux did with capabilities). You'd have to design it this way from the ground up. I think I've read about some experimental OSs exploring that kind of capability by design, almost like a type 1 hypervisor but for processes.
One problem that I have with fine-grained ACLs is that they can unintentionally add security risk, because sometimes those finer grained controls can be exploited to gain additional privledges.
If I grant something root, I know what that means and I'll be very careful. But if I grant something permission X thinking I'm safe, and then it can be used to gain permission Y, or even root, then I can be accidentally exposed.
There is just a much larger surface area to guard against, ensuring that each granular permission can't be so exploited.
It's all a challenge whether you have fine-grained permissions or coarse-grained permissions.
With coarse-grained permissions you end up needing proxies, which is nice because you can do whatever you want in terms of business logic, but also very much not nice because you have to write the proxies and the client code to talk to them, and then you have to keep maintaining and extending them.
Either way you have to do audits and static analysis looking for escalation vectors, and that's strictly harder -but not insurmountable- with fine-grained permissions.
So I think fine-grained permissions win.
I've implemented (and I'm trying to get approval to publish a paper on) a cross between RBAC-style and SMACK-style labeled security (where the labels are literal human-readable strings, not MLS-style bitmaps + levels) for application-level authorization, but it's very fast and should work in-kernel too if anyone wanted to make it work there. This system lets you have authorization as fine- or coarse-grained as you want by using many distinct labels (fine-grained) or few labels (coarse-grained) to label the application (or kernel) objects with.
For this to work, Linux needs a centralized way of managing caps. Review (or diff) the file and know immediately what's changed, instead of looking at ACLs all over the place.
That's totally true, you actually have examples of unsafe capabilities delegation in the other article mentioned in the References: https://juggernaut-sec.com/capabilities/
WhyNotHugo|4 months ago
For example, you can pass a program a capability to bind any privileged port, but not a specific one. For this scenario, just passing an fd bound to the port is actually much simpler and safer. For other capabilities, they’re just too coarse.
The fact that capabilities are implicitly inherited also doesn’t sound like a good approach on security. It’s likely like this due to backward compatibility, but I really think that capabilities ought to be passed explicitly, and we should be able to transfer them between processes. In fact, using an fd as a handle for capabilities would probably be a much clearer and explicit approach.
ajross|4 months ago
The setuid binaries already existed, and this was a means to making them (much) more secure without API changes.
btilly|4 months ago
ploxiln|4 months ago
qu4z-2|4 months ago
cryptonector|4 months ago
surajrmal|4 months ago
PaulHoule|4 months ago
https://en.wikipedia.org/wiki/Capability-based_security
that you had in AS/400 or the iAPX 432 where a "capability" is a reference to a system object with associated privileges. It is possible to get this into a POSIX-like system
https://en.wikipedia.org/wiki/Capsicum_(Unix)
It reminds me of using a VAX-11/730 with the VMS operating system in high school where there was a long list of privileges a process could have
https://hunter.goatley.com/vax-professional-articles/vax-pro...
and it was a common game to investigate paths such as "if you have privilege A, B, and C you can get SETPRV and take over the machine"
monocasa|4 months ago
They're closer to apple entitlements, but inherited through through forks rather than being attached to a binary.
rootnod3|4 months ago
Next after that I’d vote for FreeBSD’s capsicum.
marcosdumay|4 months ago
The one thing that makes capabilities usable is that they don't need to follow that rule.
If you don't have processes that let your programs get capabilities from any source other than their creation, you are better just adding your program names into your ACLs.
candiddevmike|4 months ago
ori_b|4 months ago
Nifty3929|4 months ago
If I grant something root, I know what that means and I'll be very careful. But if I grant something permission X thinking I'm safe, and then it can be used to gain permission Y, or even root, then I can be accidentally exposed.
There is just a much larger surface area to guard against, ensuring that each granular permission can't be so exploited.
cryptonector|4 months ago
With coarse-grained permissions you end up needing proxies, which is nice because you can do whatever you want in terms of business logic, but also very much not nice because you have to write the proxies and the client code to talk to them, and then you have to keep maintaining and extending them.
Either way you have to do audits and static analysis looking for escalation vectors, and that's strictly harder -but not insurmountable- with fine-grained permissions.
So I think fine-grained permissions win.
I've implemented (and I'm trying to get approval to publish a paper on) a cross between RBAC-style and SMACK-style labeled security (where the labels are literal human-readable strings, not MLS-style bitmaps + levels) for application-level authorization, but it's very fast and should work in-kernel too if anyone wanted to make it work there. This system lets you have authorization as fine- or coarse-grained as you want by using many distinct labels (fine-grained) or few labels (coarse-grained) to label the application (or kernel) objects with.
M95D|4 months ago
Traditional unix /etc/group style.
Harvesterify|4 months ago
HugoTea|4 months ago