Revokable Capabilities

A common SecurityPattern.

There's two generic methods to implement revokable capabilities.

Note, we're talking here about revoking limited caps, see TwoKindsOfCapabilities, because revoking unlimited caps is a recipe for chaos.

The first method is to give out time-limited capabilities only. That way, the recipient has to periodically renew the capability. An ingenious variation of this is to provide capabilities to a specific version of an object only. Using the variation, you get to control any modifications to the object without deluding yourself that whoever you gave the caps to hasn't made backup copies.

The second method is to request an unlimited capability of an object before handing out a limited capability to the object you so receive. If the limited capability can't be duplicated somewhere else without triggering the creation of a second unlimited capability towards the target object, then this scheme is safe. This method doesn't suffer the disadvantage of leaving users behind or forcing them to get back in touch with you at periodic intervals.

The third method is to have a level of indirection, like the CaretakerPattern.

The second of these is exactly CaretakerPattern as described above (although it's not clear what "if the limited capability can't be duplicated somewhere else" is about). The first is a variation in which the caretaker automatically revokes after a given time.

Timed revocation has the problem usually associated with timeouts; essentially, that there is no correct timeout period. In this case, a period that is too long allows an attacker to do too much damage, and a period that is short enough to prevent that will result in serious usability issues (particularly problematic is what to do about operations that were in progress at the time of the revocation -- more of an issue for timed revocation because it can occur in normal operation).


Caretaker Pattern?

Sorry, RevokableCapabilities has nothing to do with CaretakerPattern. You simply don't understand how that works. -- RK

"The second method is to request an unlimited capability of an object before handing out a limited capability to the object you so receive."

When done for the purpose of enabling revocation, this is the CaretakerPattern. If you think otherwise, please explain how it differs. -- DavidSarahHopwood

CaretakerPattern is simply not necessary to the implementation. If you implement your capabilities using the caretaker pattern, then following the protocol I've outlined above is something you do on top of the capabilities. If you don't implement your capabilities using the caretaker pattern at all, because you implement them as primitive object references in your Object Memory, then you're not using the caretaker pattern at all.

Building in the CaretakerPattern to the implementation of a capability system is a possibility, but certainly that is not required, and not how most capability systems work. One of the main points of the ParadigmRegained paper is that capability systems are well-suited to implementing security abstractions in application or library code, so that they don't have to be built in.

You misunderstand, again. The caretaker pattern isn't a security abstraction. It's a security IMPLEMENTATION. Whether you use it or not to implement your security abstractions is simply irrelevant.

Almost all patterns describe implementations. However, CaretakerPattern is pretty generic:

 "... grants a capability by transparently forwarding messages to a specified receiver (the body),
  for the purpose of making the capability revocable (for example by later setting its reference
  to the wrapped receiver to null)."

How else would any possible implementation of a limited capability work, other than by forwarding messages to the original capability? In order to satisfy PrincipleOfLeastAuthority, the requirement is to be able to generate the limited capability from the original capability without any other input, and so there is nothing else that its implementation can do.

That's all fine and dandy except that I DO NOT TRANSPARENTLY FORWARD MESSAGES. In my model, a capability is a concrete user-visible object and what it points to is a completely separate user-visible object. A capability can forward a message but it never does so transparently. And the reason why that is so is bloody simple; the capability must be ADDRESSABLE if you're to be able to ask for its history or change its PermissionFlags.

The description of the "second method" above does not say anything about reifying capabilities as objects separate from the object they point to. Certainly this is possible, but if the pattern assumes a capability system of this type, it should say so.

A caretaker can have one interface that transparently forwards messages, and another that responds to revocation requests. (The other interface might be a separate object, but that's a system-specific detail.) The same applies to objects that implement other security abstractions besides revocation. So the use of transparent forwarding is independent of whether it is possible to separately reference/address another interface of the security abstraction.

We can achieve the goals of being able to ask for the history of a capability or change its flags in a conventional cap system, without needing to reify capabilities directly. Just as CaretakerPattern handles revocation, similar applications of HandleBodyPattern variants can provide other facilities such as:

(These may or may not be done by the same object; as pointed out on the HandleBodyPattern page, instances of that meta-pattern can easily be composed.)

To anticipate your next objection -- yes, using HandleBodyPattern variants for all of the above is only one possible implementation. However, it stays within the ObjectCapabilityModel implemented by existing cap systems such as ErosOs and EeLanguage. A pattern that can be applied to several systems is more useful than one that is specific to a new, unimplemented design.

I'll note first that prototype languages are superior to class-based languages. (Incidentally, this is because having delegation as a primitive allows the construction of more powerful wrappers.) As a result, copying of objects is better than instantiation. What this means is that you should NOT have a separate object to handle revokation anymore than you should have one to handle generation.

I agree that delegation allows the construction of more powerful wrappers. This is due to the self argument and the fact that parent slots can change at run-time, not to the use of copying. I.e. the increased expressiveness of prototype-based languages does not come from their use of copying, and copying is not better in general than other methods of instantiation that are compatible with delegation (such as consistently using factory functions). Revocation does not necessarily have to be handled by a separate object, but it must be handled in such a way that it is possible to express a capability that only allows to revoke some other capability. Using a separate object is one way to do that, which stays within the standard ObjectCapabilityModel.

Further, it really doesn't matter if you can transparently forward because transparent forwarding is simply a Bad Idea. The objects of interest in the graph are not the containers that hold the caps (non-leaf nodes) nor are they even the values at the end (leaf nodes) but the capabilities themselves (edges). Of course, if you've got a primitive capability model, then this will be very different, but in a high level cap model, it's the caps that matter.

All that matters is that it is possible to refer to both the object representing a resource, and the abstraction that controls access to the resource. In the model you are advocating, that is done by making both nodes and edges in the reference graph FirstClass. In the model I'm advocating, the same effect is achieved by putting a proxy around every resource object and referencing either the proxy's forwarding facet or a separate control/revocation facet. You haven't given any reason for this being a BadIdea (other than reasons I've already refuted).

All objects in a design should be FirstClass. This is really design 101. Thank you for giving me the ammo I needed for my response.

"All objects in a design should be FirstClass." Not necessarily. There are indeed many systems that fail to make various things first-class when they should be, and that suffer badly as a result. In this case, though, both models can refer to exactly the same set of things. The proxy-based approach just makes the things that need to be referred to into nodes, instead of edges. (Strictly speaking, in the ObjectCapabilityModel it is only the edges that are first-class, since edges refer to nodes and can be used as values.) There is no loss of expressiveness; on the contrary, there is a gain in expressiveness because new or changed security abstractions can be implemented outside the system TCB.

The problem is that you have a TCB and I don't. You have a kernel and I don't. Your proxies are a workaround for a lack of design and my high level caps aren't. I don't need to create proxies because I don't have a kernel/TCB to be suspicious of. And here's the killer, I'm repeating myself here, to create a new permission flag, all a user of my system has to do is, literally, to just decide to use it. To create a new proxy in your system, with some new combination of abstractions, you need to code up the proxy (so already your system is excluding users -- nearly every normal user) and then they even have to distribute it!! You call that power? Bullshit. I call it being a programming weenie.

If you think my system isn't perfectly general up front where yours needs the hindsight of loser programmers, then you just aren't a designer. Makes me wonder why you wish to converse with one, or indeed discuss the subject of design.

Finally, forwarding is sufficiently simple that it can't be made much simpler than it is. Just how complex is 'output pass: aSymbol to: aCollectionOfStrings with: anArray perms: aCollection', where the symbol is the ultimate message selector, the strings are the names of the caps in the path, the array contains the arguments to the ultimate message, and perms keeps track of the PermissionFlags the message has acquired on the way. So just HOW are you going to make this SIMPLER? Do you really need to be able to write "output 'capA' 'capB' 'capC' method: args"? Hell yeah! But that must be done in the language if you're going to avoid returning every single intermediate result (perhaps over a network) and doesn't need to be built into the cap scheme at all.

Note the beauty of it though. In Smalltalk, method selectors are symbols yet cap names are strings. As long as you can keep the two separate, it's possible to transparently forward. But if you can't then tranparent forwarding is a bunch of crap that limits the space of caps' names by conflating cap names with cap functions. Because how would you ever address ('capA' 'capB' 'revokeSelf') if 'revokeSelf' is a method name of capB? You wouldn't be able to.

You seem to be assuming a lot of context about BlueAbyss that you haven't explained, but that doesn't seem to be relevant to the approach I'm suggesting. In the ObjectCapabilityModel (and in Smalltalk), caps/references don't have string names. A string-named variable in a given lexical context may hold a reference, but that's not the same thing.

How can a revocation facet be obtained, given that a reference to the forwarding facet does not allow revocation? The answer to that depends on policy decisions about who can revoke what. Suppose, for example, that capabilities can belong to groups (with a similar purpose to UserDomains?) such that having a particular permission on the group allows revoking any of the capabilities in it. Then, we can obtain a revocation facet by asking the group object for it (passing in the corresponding forwarding facet to identify the capability that we will want to revoke). This does not require any use of string names (other than variable names).

The problem is that every one of the functions I have in my self-consistent and integrated scheme, you provide using your own completely ad-hoc proxy. You don't have this and this? Oh, that's all right, just invent a proxy to do it.

Yes, exactly -- any security abstraction that I want to provide, I can provide either by adding a proxy, or by changing the default proxy. You are using "ad-hoc" simply as a content-free term of abuse. What, precisely, makes this use of proxies "ad-hoc", as opposed to elegant and powerful?

Then you have all these orthogonal ugly-ass proxies floating around with absolutely no coherent design or aforethought given to them.

Don't be ridiculous. I haven't explained all of the thought I have put into what security abstractions are needed and how they interact, any more than you have. I am just not assuming that I can do BigDesignUpFront.

Oh, but I have. Other people's inability to understand my explanations don't negate the existence of my explanations. I've explained in exquisite detail, whereas you've given fig all.

And finally, to go back to your ignorant complaint that I reify my caps as objects, this is not at all necessary. As you should know by now from other pages, I intend to build caps into my object format and have the virtual machine understand them when it performs message sends. You can't complain about my doing it and then ignore that I'm going to do it.

That is an unimportant implementation issue.

No, it isn't. On the one hand, my references are equally first class as my nodes. On the other hand, neither of them are objects. But we're very much at the stage of mostly ignoring each others' points.

The only way to argue that all capabilities systems everywhere use the caretaker pattern is to define that pattern so broadly that it becomes vacuous.

The definition is pretty clear. It is not vacuous.

Further, all access abstractions can be built from capabilities. That's because capabilities are universal. [...] All possible control structures can be built from GOTOs, yet only a retard would deny that GotoConsideredHarmful. The fact of the matter is that there are PERVASIVE secure access patterns which it is completely idiotic to not build in at the lowest possible layer. This isn't the first time I make this point, nor even the second or third.

What is important is the interface presented to typical users of resources, not how many layers are used to implement that interface.

There will always be some fucking cowboy, usually some system programmer, who will set out to destroy everything under the excuse that "their" code needs the extra speed advantage of directly using lower-level abstractions than those used by mere users'.

Anyone who does that will only mess up the security of "their" code, not anyone else's code. It is not possible for the 'cowboy' to bypass abstraction layers surrounding any object with an implementation that they didn't write or modify. And to support adversarial code reviews, it should only be possible to bypass the low level abstractions if it is blindingly obvious in the code that this has been done.

Of course if a programmer responsible for part of the system TCB bypasses necessary abstractions, they can mess up the whole system. But that is true of any system. DontDoThat. The system TCB should be as small as possible and all changes to it should be reviewed by several people, at least, anyway.

And of course, such an asshole will be too fucking lazy to reflect multiple fake layers to users. This is made much worse when, as is typical of systems that make necessary abstractions "optional", you make it a thousand times easier to simply avoid using the necessary abstractions which everyone must use, ALL of the fucking time.

{{See LiterateAlternativesToVulgarity}}

I agree that it will be necessary to use abstractions that provide basic facilities like revocation, logging, etc. "all of the fucking time". This does not mean that it will be sufficient to use the same implementation of these abstractions "all of the fucking time". Besides, there would be benefits in using a layered architecture even if the same implementation were always used.

How often is it necessary for users to mess with the Smalltalk VM or image format? Almost never. Why? Because they were well designed. The same is true of a cap scheme. If you've got one that's sufficiently general and well-designed, you can sink it down into the VM and object format, and almost nobody will feel the need to change it. Conversely, if you're the chowderhead who made C++ or Java then everyone will want to rework your abstractions. If you're really worried about sinking caps to such a low level then just use a language that's dynamic enough that you can redefine message sends.

Great -- let's make the security of the system absolutely dependent on getting the design right before we have any feedback on how well it works in real life. Sounds like a wonderful idea.

What part of what I wrote didn't you understand? Oh yeah, all of it.

And why must these abstractions be used all of the time? Because users will find ways to use/reuse your objects in ways Albert Einstein would be too stupid to ever imagine. And when they do, they will NEED that security. They will NEED to, for example, change a limited capability to an unlimited capability, and vice versa, at the flick of a switch. And that's simply impossible if you've built these things at completely different layers of abstraction.

That last sentence is just not true, in general. It depends on the detailed design of these layers.

But it doesn't matter because this is besides the point. The point being that revoking capabilities the way that I described it above is completely independent of any caretaker pattern. Unless you wish to argue that the Object Memory is a caretaker, but that'd just render the distinction meaningless.

You still haven't described the intended pattern clearly enough for me to determine whether that is the case.


Here's how the protocol works. A limited capability is a stick, and an unlimited capability is a gun. You ask me for a stick, and I give it to you only after you've given me a loaded gun. You can use that stick on me only for however long and in whatever manner I wish. When it comes time to revoke it, I can shoot you dead or I can try to knock out the stick from your hands at my leisure. The limited/unlimited method of revokation means that the object which requests a capability puts itself at the mercy of the object that grants the capability. --rk

Much unclear argument snipped.

If you have a cap to A which has a cap to B, you can ask capA to send a message to capB asking to copy itself. If you have a cap to B, you just ask that cap directly. Assuming capB even receives the message, it will copy itself with the permissions collected in the message modulated by the permissions it holds. This scheme works whatever kind of cap you hold, revokability being just one of the standard permissions in GrandUnifiedCapabilities, also known as 'override'.

This bears little or no relation to what you said originally. In particular, I still don't get what you were on about with "The limited/unlimited method of revokation means that the object which requests a capability puts itself at the mercy of the object that grants the capability." That's what I was objecting to, since it would have been a PolaViolation.

No, it's exactly what I wrote originally. Someone had rewritten it in order to match their own idiotic misconceptions. And what I've written here, there and everywhere is consistent with itself, including requesters putting themselves at the mercy of the objects they request stuff from. And as a POLA violation, it's an insignificant and trivial one.

Here's a clue: in real world design, the nice shiny theories of academia turn out to be so much useless and oversimplified crap. The two certain giveaways that you don't do a lick of design (or science) is when you combine blind worship of first-hand experience with blind worship of mathematical models. Correction, the giveaway is when you have one or the other. When you have both it's simply sick.

Mindlessly anti-academic claptrap.

A security model is not weak or incomplete just because it violates POLA. POLA is just a heuristic, good for people who are starting out in security design. See level 3 of ThreeLevelsOfAudience for my take on it. And on the FourLevelsOfCompetence, I'd score you as a 2, whereas I've passed 3 a long time ago.

If someone had rewritten the explanation, why didn't you just correct it rather than leading me to waste time criticising it? As rewritten, it was clearly a severe POLA violation. And yes, I'm aware that POLA is just a heuristic. BTW, I don't care about your opinion of your own competence, or mine.

In any case, it appears that your approach really is, as I thought originally, equivalent to an instance of CaretakerPattern. Your reified caps are acting as caretakers; the fact that their logic is implemented by anding permission flags rather than doing a boolean test is a trivial difference. The only other difference (which is important, but not to the issue of whether CaretakerPattern is being used) is that the caretakers are built-in.

And here we go again, all the way back to the beginning. Caretaker pattern is not being used if it's built in.

Nonsense. Is this the only reason you have for claiming that CaretakerPattern isn't being used?

If you define the pattern to include all cases of indirection then it really means abso-fucking-lutely nothing at all. The fact that, again, you blindly worship (a concept / term) something which is completely useless (for thinking / communicating), is just evidence that you're a complete ninny.

This discussion is useless as you're not the kind of person which it is fruitful to have a discussion with. At least, not on this topic. This discussion is over.


[[What prevents object A from cheating by, say, asking for a cap to object B on behalf of object C who will store the cap somewhere inaccessible?]]

It is not possible or necessary to prevent delegation in the "Communicating Conspirators" case (see http://www.erights.org/elib/capability/delegations.html ).

Unpaired caps are self-revoking by providing access to only the current and past (at the time the cap was handed out) versions of the object. -- rk

IOW, there's little or no need for revocation of access to pure-functional objects.

That's right. Fortunately, pure-functional languages are something of a rarity. I say fortunately because the human mind simply isn't built to understand the functional paradigm. -- rk

I strongly disagree, but that's off-topic for this page.


See also CapabilitySecurityModel.


CategorySecurity


EditText of this page (last edited September 30, 2009) or FindPage with title or text search