EmbeddedRelated.com
Forums
Memfault Beyond the Launch

Managing "capabilities" for security

Started by Don Y November 1, 2013
Hi,

Not sure exactly how I want to ask this question;
i.e., how best to differentiate the examples where
X should be allowed vs X should be prohibited.

I have a capabilities based security model.  Each
capability has "authorizations" associated with it
(trying to avoid using the word "capability", again  :< ).

These authorizations are defined by the entity that
creates the capability based on the "authorizations"
that *it* has available to it!

I.e., if I own a resource, I have all of the authorizations
conceivable for that resource.  I can give all or part of
those authorizations to entities (actors) of my choosing.

E.g., if the resource is a file, I could elect to give
A, B and C read access to that file and write access
only to B and D.

Similarly, if the resource is a mechanism, I might give the
ability to move it RIGHT to A, B and D; the ability to move
it LEFT to B and C; the ability to power it OFF to only A;
etc.

It's important to be able to give subsets of your "authorizations"
to others -- that you presumably trust (whatever that means).
This allows them to act on your behalf.

E.g., if I have read & write access to a file, I might want to
give *read* access to that file (principle of least privilege)
to someone who will encrypt it's contents for me (returning
an encrypted copy of the file but not altering the original;
I trust him enough to *see* the file's contents but not enough
to allow him to *alter* them -- to, for example, replace the
file with its encrypted form... *I* can do that with my write
authorization).

Similarly, I might want to give subsets of my authorizations
to several different actors concurrently -- so each can do
"whatever" to the resource without requiring me to serialize
their accesses to it (multiprocessing)

And, I may also want to *forfeit* my authorizations -- possibly
after passing them on to someone else.

OK?

Some of the trickier issues I'm trying to address include:

- "revoking" an authorization that I have previously given
   to another actor (do I do this asynchronously?  synchronously
   with the other actor's consent/participation?  etc.)
- handling intermediaries whose roles are strictly as "pipes"
   (e.g., imagine transparently imposing an actor between A
   and B -- call it D -- to allow the transactions between those
   two actors to be Debugged).  D should have no need to invoke
   any of the authorities associated with any capability passed
   from A to B.  It should be restricted to solely *propagating*
   the capability.  I.e., D can't *hold* that capability but
   can pass it along.
- as a followup to the above, handling cases where the capability
   can be held or propagated -- but not *duplicated*.  I.e., *you*
   can access this file; *or*, have someone else do it on your behalf;
   but it's one option or the other... the capability can't multiply!

The goal here is to allow *most* actors to be untrusted and still
minimize the risk they pose to the system, the data and operations
it implements, etc.

E.g., I could create a resource called an "email address".  I could
define operations on that like "send to", "forward to", etc.  And,
I can choose to make the actual address itself, *opaque*!

So, I can create a capability for this resource that has authorizations
for "send to".  Perhaps there's even a "send exactly once"!  I can
now give that capability to an actor and it would be able to send
email *to* that address -- yet, never *see* the address itself!
So, if it was a rogue agent trying to harvest email addresses from
ever device that it was running on, this ability would be thwarted.

To protect against it generating neverending quantities of spam, I
might opt to give it the "send exactly once" aauthorization knowing
that it's damage/annoyance factor would be thusly limited.

If this actor can pass subset(s) of its capability to others, then
it could just spawn another copy of itself, send a copy of the
capability to that second instance, generate a piece of spam and
*die* -- knowing it's clone has a valid copy of the "send exactly
once" authorization!

See where this is going?  And, how powerful it can be in providing
fine-grained control of resources??

It *seems* like what I really want to do is create a "service" (for want
of a better word) that implements these "capability authorizations".
I.e., if you want to pass a copy of a capability to another actor,
you hand the capability to that service along with the desired target
actor and that service examines the capability to see the authorizations
that you have been granted for *it*!

"Ah, sorry but I can't perform this action for you because you
don't have 'propagate a copy' authorization for this capability!"

[<frown>  This is really confusing due mainly to the fact that I'm
trying to fabricate terms to address concepts that are very similar
but different -- capability, authorization, etc.]

Hopefully this makes *some* sense...  I'll try to work on a better
lexicon.

--don
On 01/11/13 21:34, Don Y wrote:
> Hi, > > Not sure exactly how I want to ask this question; > i.e., how best to differentiate the examples where > X should be allowed vs X should be prohibited.
I can't see that you've been asking a question at all - it looks more like you have some ideas about what you think "capabilities" are and are trying to get a clearer picture. But I don't think your post here is ready for direct comments - you'll have to read a bit more, think a bit more, and figure out what you are trying to say, trying to ask, and trying to do. In the meantime, read up a bit on "posix capabilities" and their implementation in Linux: <http://en.wikipedia.org/wiki/Capability-based_security> <http://man7.org/linux/man-pages/man7/capabilities.7.html> <http://en.wikipedia.org/wiki/Tahoe_Least-Authority_Filesystem> (and of course, google is your friend :-) I don't think this is the kind of stuff you want to do yourself - there are a great deal of things to get right for tasks to have enough access to what they need without opening security holes. mvh., David
> > I have a capabilities based security model. Each > capability has "authorizations" associated with it > (trying to avoid using the word "capability", again :< ). > > These authorizations are defined by the entity that > creates the capability based on the "authorizations" > that *it* has available to it! > > I.e., if I own a resource, I have all of the authorizations > conceivable for that resource. I can give all or part of > those authorizations to entities (actors) of my choosing. > > E.g., if the resource is a file, I could elect to give > A, B and C read access to that file and write access > only to B and D. > > Similarly, if the resource is a mechanism, I might give the > ability to move it RIGHT to A, B and D; the ability to move > it LEFT to B and C; the ability to power it OFF to only A; > etc. > > It's important to be able to give subsets of your "authorizations" > to others -- that you presumably trust (whatever that means). > This allows them to act on your behalf. > > E.g., if I have read & write access to a file, I might want to > give *read* access to that file (principle of least privilege) > to someone who will encrypt it's contents for me (returning > an encrypted copy of the file but not altering the original; > I trust him enough to *see* the file's contents but not enough > to allow him to *alter* them -- to, for example, replace the > file with its encrypted form... *I* can do that with my write > authorization). > > Similarly, I might want to give subsets of my authorizations > to several different actors concurrently -- so each can do > "whatever" to the resource without requiring me to serialize > their accesses to it (multiprocessing) > > And, I may also want to *forfeit* my authorizations -- possibly > after passing them on to someone else. > > OK? > > Some of the trickier issues I'm trying to address include: > > - "revoking" an authorization that I have previously given > to another actor (do I do this asynchronously? synchronously > with the other actor's consent/participation? etc.) > - handling intermediaries whose roles are strictly as "pipes" > (e.g., imagine transparently imposing an actor between A > and B -- call it D -- to allow the transactions between those > two actors to be Debugged). D should have no need to invoke > any of the authorities associated with any capability passed > from A to B. It should be restricted to solely *propagating* > the capability. I.e., D can't *hold* that capability but > can pass it along. > - as a followup to the above, handling cases where the capability > can be held or propagated -- but not *duplicated*. I.e., *you* > can access this file; *or*, have someone else do it on your behalf; > but it's one option or the other... the capability can't multiply! > > The goal here is to allow *most* actors to be untrusted and still > minimize the risk they pose to the system, the data and operations > it implements, etc. > > E.g., I could create a resource called an "email address". I could > define operations on that like "send to", "forward to", etc. And, > I can choose to make the actual address itself, *opaque*! > > So, I can create a capability for this resource that has authorizations > for "send to". Perhaps there's even a "send exactly once"! I can > now give that capability to an actor and it would be able to send > email *to* that address -- yet, never *see* the address itself! > So, if it was a rogue agent trying to harvest email addresses from > ever device that it was running on, this ability would be thwarted. > > To protect against it generating neverending quantities of spam, I > might opt to give it the "send exactly once" aauthorization knowing > that it's damage/annoyance factor would be thusly limited. > > If this actor can pass subset(s) of its capability to others, then > it could just spawn another copy of itself, send a copy of the > capability to that second instance, generate a piece of spam and > *die* -- knowing it's clone has a valid copy of the "send exactly > once" authorization! > > See where this is going? And, how powerful it can be in providing > fine-grained control of resources?? > > It *seems* like what I really want to do is create a "service" (for want > of a better word) that implements these "capability authorizations". > I.e., if you want to pass a copy of a capability to another actor, > you hand the capability to that service along with the desired target > actor and that service examines the capability to see the authorizations > that you have been granted for *it*! > > "Ah, sorry but I can't perform this action for you because you > don't have 'propagate a copy' authorization for this capability!" > > [<frown> This is really confusing due mainly to the fact that I'm > trying to fabricate terms to address concepts that are very similar > but different -- capability, authorization, etc.] > > Hopefully this makes *some* sense... I'll try to work on a better > lexicon. > > --don
Hi Don,


On Fri, 01 Nov 2013 13:34:05 -0700, Don Y <this@isnotme.com> wrote:
> >Some of the trickier issues I'm trying to address include: > >- "revoking" an authorization that I have previously given > to another actor (do I do this asynchronously? synchronously > with the other actor's consent/participation? etc.)
Revoking always should be asynchronous because it is solely at the discretion of the giver. If a capability can be delegated transitively, the originating authority may neither know nor be able to communicate directly with all of the current holders of the capability. An agent can't simply hand out a copy of an original capability given to it - it needs to pass on a derived capability that is separate from but linked to the original. The derived capability has to be revocable both independently (by the agent itself) and in conjunction with revocation of the original capability (by the originating authority). How to handle transitive delegation is *the* major issue in designing a capability system.
>- handling intermediaries whose roles are strictly as "pipes" > (e.g., imagine transparently imposing an actor between A > and B -- call it D -- to allow the transactions between those > two actors to be Debugged). D should have no need to invoke > any of the authorities associated with any capability passed > from A to B. It should be restricted to solely *propagating* > the capability. I.e., D can't *hold* that capability but > can pass it along.
Yes. A communication channel should not make information observable to any entity that is not a participant in the communication. A debugger monitoring a channel, and possibly injecting traffic into it, is a special case of a "silent" participant.
>- as a followup to the above, handling cases where the capability > can be held or propagated -- but not *duplicated*. I.e., *you* > can access this file; *or*, have someone else do it on your behalf; > but it's one option or the other... the capability can't multiply!
Just a particular case of transitive delegation.
>See where this is going? And, how powerful it can be in providing >fine-grained control of resources??
Nope and Nope 8-) The problem is that such flexible capabilities effectively become stores of arbitrary key-value pairs. That makes them difficult to manage and difficult to propagate (or migrate) to remote hosts. I know you are using a (more or less) centralized DBMS - which solves the migration problem - but you still have the issue of how to organize the DB so that capabilities are easy to modify, copy or subset, *and* can be rapidly searched. Haven't given it a lot of thought, but I don't immediately see a good answer. You know I'm not a big fan of non-TEXT extensible fields because (usually) they can't be indexed. Once you start down the road of using arbitrary capabilities, like ACLs, very quickly you find that you have many thousands of {capability,name,setting} triples in use.
>It *seems* like what I really want to do is create a "service" (for want >of a better word) that implements these "capability authorizations". >I.e., if you want to pass a copy of a capability to another actor, >you hand the capability to that service along with the desired target >actor and that service examines the capability to see the authorizations >that you have been granted for *it*!
It seems like what you want to do is have a communication system that can open a channel *and* deliver the access capability for it in a single action. Once a channel is open, you can communicate other capabilities directly. E.g., on the client side, connect() creates both a new connection and a capability for it, and transmits the capability to the accept() on the server side. Depending on your programming model, both may also return the capability to their respective callers - e.g., for delegate communication. Within a host you can start a child task and transfer capabilities automagically during the fork(). You can do similar using a process server on a remote host, communicating the capabilities to the server and letting it fork and install them before exec()ing the new process. [Naturally, after providing proof of _your_ capability to remotely launch the process.]
>"Ah, sorry but I can't perform this action for you because you >don't have 'propagate a copy' authorization for this capability!"
ISTM that a system which rejects this particular use case is not realistic. If you can't start a task which isn't running, or open communication with a task that is, then the system is useless.
>--don
YMMV, George
Hi George,

[Apologies if more typos than usual -- pen interface :( ]

On 11/4/2013 1:20 AM, George Neuner wrote:
> On Fri, 01 Nov 2013 13:34:05 -0700, Don Y<this@isnotme.com> wrote: >> >> Some of the trickier issues I'm trying to address include: >> >> - "revoking" an authorization that I have previously given >> to another actor (do I do this asynchronously? synchronously >> with the other actor's consent/participation? etc.) > > Revoking always should be asynchronous because it is solely at the > discretion of the giver.
Yes, of course [re: giver]. What I was trying to draw attention to is how The "holder" is effectively "made aware" of his loss of some/all of the "authorizations" that he had previously. (more below)
> If a capability can be delegated > transitively, the originating authority may neither know nor be able > to communicate directly with all of the current holders of the > capability.
Unlike, (e.g.) Amoeba (apologies if I am misremembering references/ implementations, here) -- where a capability is "just a (magic) number (which can obviously be copied FREELY and indefinitely) -- I implement capabilities as kernel based/maintained objects. What a task sees as a "capability" is actually a *handle* to a capability (that the kernel tracks). [E.g., Amoeba's implementation doesn't require the kernel to be aware of where a capability "is", currently. It is only aware when operations *on* the capability need to be performed (reminder: Amoeba makes the capabilities UNFORGEABLE and little more).] As my kernel knows where every capability is located, at the moment, if can deliver an asynchronous notification ("signal") to the holder of the capability (holder == task; so no guarantee which of the task's threads will "see" that notification -- unless an exception handler thread has been nominated). So, I *can* notify the holder. Or, wait for him to try to use the capability and throw an error at that time. Of course, either approach can work. What I'm trying to decide is the relative merits of each -- on both sides of that notification fence!
> An agent can't simply hand out a copy of an original capability given > to it - it needs to pass on a derived capability that is separate from > but linked to the original.
As holders only have handles to capabilities, I actually *can* "move" the original capability. If a holder elects to create a *new* capability embuing it with some formal subset of the "authorizations" that are present in the original capability, it can (potentially!) do so. [that's one of my conceived restrictions...]
> The derived capability has to be > revocable both independently (by the agent itself) and in conjunction > with revocation of the original capability (by the originating > authority). > > How to handle transitive delegation is *the* major issue in designing > a capability system.
<grin> Hence the reason for my questions! They (below) pertain to he sorts of operations you can perform *on* the capability. E.g., the Amoeba approach allows the holder to freely copy and distribute *a* capability that it holds. It has to trust EVERY recipient of those capability-copies. And, implicitly, any one that *they* may conceivably pass yet other copies to! [in my case. I can implement mechanisms that allow you to create copies *or* restrict you to passing *the* original along. I.e., the valet can drive my car while he has the keys -- but, once he gives them to a *thief*, he loses that ability!]
>> - handling intermediaries whose roles are strictly as "pipes" >> (e.g., imagine transparently imposing an actor between A >> and B -- call it D -- to allow the transactions between those >> two actors to be Debugged). D should have no need to invoke >> any of the authorities associated with any capability passed >> from A to B. It should be restricted to solely *propagating* >> the capability. I.e., D can't *hold* that capability but >> can pass it along. > > Yes. A communication channel should not make information observable > to any entity that is not a participant in the communication. > > A debugger monitoring a channel, and possibly injecting traffic into > it, is a special case of a "silent" participant.
In the example I cited, should I have to trust D as much as I do B? If I allow it to create a copy (for its own use) of the capability, then I do. OTOH, if I create a "pass all or nothing" attribute for the capability, then the only way that D can use the capability is by denying it to B. (see where I'm going, here?)
>> - as a followup to the above, handling cases where the capability >> can be held or propagated -- but not *duplicated*. I.e., *you* >> can access this file; *or*, have someone else do it on your behalf; >> but it's one option or the other... the capability can't multiply! > > Just a particular case of transitive delegation.
But, IMO, the ability to do this effects the security that the capability system provides. If you can always duplicate a capability (or portions thereof), then you have to always trust everyone you give it to. While I may trust you not to abuse a capability, do I also know that you can't be tricked (bug) into passing a copy of that capability on to an adversary?
>> See where this is going? And, how powerful it can be in providing >> fine-grained control of resources?? > > Nope and Nope 8-) > > The problem is that such flexible capabilities effectively become > stores of arbitrary key-value pairs. That makes them difficult to > manage and difficult to propagate (or migrate) to remote hosts.
I can move them in much the same way that I can move all my "objects" -- kernel(i) and kernel(j) conspire to track any particular capability (cuz it is implemented *in* the kernel) It's just not *efficient* to do so (vs. the "capability is a magic integer" approach of e.g., Amoeba. [From our discussions, you should already know that I'm wasting LOTS of resources on mechanisms that I think will enhance the development/execution environment]
> I know you are using a (more or less) centralized DBMS - which solves > the migration problem - but you still have the issue of how to > organize the DB so that capabilities are easy to modify, copy or > subset, *and* can be rapidly searched.
Ah, no. This is at a different level. I haven't even begun to sort out how to make capabilities "persistent" (i.e., so they could be "stored" in the RDMS). Instead, they are transient objects that only exist while the owner/holder is alive. E.g., a file handle is not, in itself, persistent -- even if the file that it *references* is (or *isn't*!) *Accessing* the RDBMS can be controlled by a capability, though.
> Haven't given it a lot of thought, but I don't immediately see a good > answer. You know I'm not a big fan of non-TEXT extensible fields > because (usually) they can't be indexed. Once you start down the road > of using arbitrary capabilities, like ACLs, very quickly you find that > you have many thousands of {capability,name,setting} triples in use.
Exactly. Though mine don't have a textual way of being expressed (as an ACL would).
>> It *seems* like what I really want to do is create a "service" (for want >> of a better word) that implements these "capability authorizations". >> I.e., if you want to pass a copy of a capability to another actor, >> you hand the capability to that service along with the desired target >> actor and that service examines the capability to see the authorizations >> that you have been granted for *it*! > > It seems like what you want to do is have a communication system that > can open a channel *and* deliver the access capability for it in a > single action. Once a channel is open, you can communicate other > capabilities directly.
Yes, in a sense. Each capability is actually an (object,authorizations) tuple (I *really* need a better word than "authorizations" :< ). Think of a file handle. It allows you (the "Holder") to access some particular file (the name of which is not deducible from the handle itself!) in some particular way. If thread A creates a handle and gives it to thread B, B must *implicitly* know what "authorizations" are embedded in that handle (i.e. if the file was opened for read-only access, thread B can't decide to *write* to it!). In my case, "somehow" (easy once you think about it), a task is given a reference (handle) to an object (on which it would like to operate *or* on which it is being *asked* to operate!) along with the authorizations as to *how* it can operate on that object. In a crude sense, it is being given a reference to an "object" and a list of the verbs/methods that it can invoke for that object (it's actually much finer-grained than this but this should give you an idea). For a traditional "file", those authorizations might include read, write, seek, etc. For a motor, they might include, CW, CCW, FAST, SLOW, BRAKE, etc.
> E.g., on the client side, connect() creates both a new connection and > a capability for it, and transmits the capability to the accept() on > the server side.
Yes. Though connect() implies that you have previously been given the authorization to connect to that object! :> I.e., if you should have no need to talk to the file system, then you can't even *connect* to that service!
> Depending on your programming model, both may also > return the capability to their respective callers - e.g., for delegate > communication. > > Within a host you can start a child task and transfer capabilities > automagically during the fork().
Being careful about terms, here... In my case, I can create more *threads* within the current *task* (task == container of resources -- threads being execution resources). Any thread can use any of the resources (e.g., capabilities) that are present in that *task*. Tasks can also create additional tasks (the UNIX "process" model). In *those* cases, the spawned task does not automatically inherit the resources of the "parent" (whereas a thread does!). Instead, the resources that it should have are explictly passed to it. So, any threads created by threads *in* a particular task have the same resources that all threads in that task have. If you want to restrict (or change in any way) the resources available to an "execution unit", you have to create a different task.
> You can do similar using a process > server on a remote host, communicating the capabilities to the server > and letting it fork and install them before exec()ing the new process.
Yes. Though the process server is actually the kernel running thereon.
> [Naturally, after providing proof of _your_ capability to remotely > launch the process.]
The fact that I *did* launch the process is preconditioned on *having* the capability to do so!
>> "Ah, sorry but I can't perform this action for you because you >> don't have 'propagate a copy' authorization for this capability!" > > ISTM that a system which rejects this particular use case is not > realistic. If you can't start a task which isn't running, or open > communication with a task that is, then the system is useless.
That's just a bootstrap problem. I.e., *something* starts "init". Once the first "process" (task) is running, it creates the remaining tasks in the system. As each task is an "object", there is an implicit capability associated with it! Using this "handle", that first process can push an executable image into the task, give it capabilities for other resources, etc. And, each of these initial tasks (processes) can create their own resources with associated capabilities that can be handed out to still other tasks, etc. I.e., "init" builds the initial set of servers as part of the application itself. Any service that wants tight reign over its clients can hand out capabilities that it refuses to let be propagated. That doesn't prevent the recipients of those capabilities from ACTING as AGENTS for the service! So, *they* can create their own INDEPENDANT capabilities (i.e., not subsets of the original capabilities granted to themselves). So, "you" might not be able to talk to a root DNS server. But, you can talk to *me* (using an API of my own creation) and *I* can elect to contact the root DNS servers if I think that is warranted. (inefficient because I am now in the middle. But, now allows me to actively control access to that other resource, cache results, etc.) Morning tea... --don
[Apologies for following up on my own post...]

On 11/4/2013 8:23 AM, Don Y wrote:

>> How to handle transitive delegation is *the* major issue in designing >> a capability system. > > <grin> Hence the reason for my questions! They (below) pertain to he > sorts of operations you can perform *on* the capability. > > E.g., the Amoeba approach allows the holder to freely copy and > distribute *a* capability that it holds. It has to trust EVERY > recipient of those capability-copies. And, implicitly, any > one that *they* may conceivably pass yet other copies to! > > [in my case. I can implement mechanisms that allow you to create copies > *or* restrict you to passing *the* original along. I.e., the valet can > drive my car while he has the keys -- but, once he gives them to a > *thief*, he loses that ability!]
To put this in more concrete terms: I can create an object called an "email_address". Unlike the intuitive way you would think of an email address (i.e., a set of characters with an '@' in the middle), the object manager for email addresses (email_address_handler) can make these *opaque* to applications. I.e., an application can *send* something to an email address but can't tell where it is going! (i.e., you can't harvest the email addresses in my address book and pass them on to your friend The Nigerian Prince!). So, I can give you a capability that allows you to send messsages to a particular recipient: email_address_t recipient; without knowing who that is. I might opt to give you the authority to do this exactly once (if, for example, the email_address_handler allowed me to create a number_of_uses argument for a particular capability instance). "Hey, you're just supposed to be notifying these people of my upcoming party. Why do you need to be able to send more than *one* email to each recipient?" If I prohibit you from passing this along, then I am, in effect, forcing *you* to do the work (send out the invitations). If I allow you to pass it along BUT NOT DUPLICATE IT, then you can delegate a third party to perform this activity on your behalf -- but you also forfeit the ability to use that resource thereafter. I.e., you can ask your friend The Nigerian Prince to send out the invitations for you (of course, he wont be able to "see" the actual email addresses any more than *you* would). He might opt to send them all some sort of junk mail. But, he'll only be able to send one message to each recipient (because that was the constraint placed on the capability when it was originally granted to *you*!) The question then becomes: do you create some set of "operations" that can be applied to all capabilities; or, allow each capability to have a specific handler (so *it* operates on the capabilities while the capabilities operate on the objects they reference) [another indirection]
Hi David,

On 11/4/2013 12:49 AM, David Brown wrote:
> On 01/11/13 21:34, Don Y wrote: >> Hi, >> >> Not sure exactly how I want to ask this question; >> i.e., how best to differentiate the examples where >> X should be allowed vs X should be prohibited. > > I can't see that you've been asking a question at all - it looks more > like you have some ideas about what you think "capabilities" are and are > trying to get a clearer picture. But I don't think your post here is > ready for direct comments - you'll have to read a bit more, think a bit > more, and figure out what you are trying to say, trying to ask, and > trying to do. > > In the meantime, read up a bit on "posix capabilities" and their > implementation in Linux:
Different beast entirely -- in mvch the same way that "file permission bits" differ from full-fledged ACLs. From your first reference, below: "A capability (known in some systems as a key) is a communicable, unforgeable token of authority. It refers to a value that references an object along with an associated set of access rights." AFAICT, Linvx uses the term just to reference a finer grained set of "permissions" afforded to processes (beyond "root == God"). [IMO, you can't *effectively* ADD capabilities to an existing "system" except in very narrow, fortuitous places] For more info, you might want to look at Amoeba, Chorus, EROS/KeyKOS, etc. (each to differing degrees).
> <http://en.wikipedia.org/wiki/Capability-based_security> > <http://man7.org/linux/man-pages/man7/capabilities.7.html> > <http://en.wikipedia.org/wiki/Tahoe_Least-Authority_Filesystem> > > (and of course, google is your friend :-) > > I don't think this is the kind of stuff you want to do yourself - there > are a great deal of things to get right for tasks to have enough access > to what they need without opening security holes.
On 11/4/13, 3:20 AM, George Neuner wrote:
> Hi Don, > > > On Fri, 01 Nov 2013 13:34:05 -0700, Don Y <this@isnotme.com> wrote: >> >> Some of the trickier issues I'm trying to address include: >> >> - "revoking" an authorization that I have previously given >> to another actor (do I do this asynchronously? synchronously >> with the other actor's consent/participation? etc.) > > Revoking always should be asynchronous because it is solely at the > discretion of the giver. If a capability can be delegated > transitively, the originating authority may neither know nor be able > to communicate directly with all of the current holders of the > capability. >
I would disagree with this. If the use of a capability without authorization causes the requester to malfunction, then having a protocol that doesn't begin revocation with notification can make the authorization nearly worthless, as the actor using it can't be sure it is safe. You also need to make sure that you don't have "transactions" that might be corrupted with a revocation in their midst. Yes, sometimes just doing an asynchronous revocation may make sense, and in many cases having it as a fall back if the cooperation method fails to complete in a needed time is needed, but that doesn't mean that asynchronous is generally preferred. As to the transitively granting, the same method could be used to relay the request to revoke.
Hi Richard,

On 11/4/2013 7:25 PM, Richard Damon wrote:
> On 11/4/13, 3:20 AM, George Neuner wrote: >> On Fri, 01 Nov 2013 13:34:05 -0700, Don Y<this@isnotme.com> wrote: >>> >>> Some of the trickier issues I'm trying to address include: >>> >>> - "revoking" an authorization that I have previously given >>> to another actor (do I do this asynchronously? synchronously >>> with the other actor's consent/participation? etc.) >> >> Revoking always should be asynchronous because it is solely at the >> discretion of the giver. If a capability can be delegated >> transitively, the originating authority may neither know nor be able >> to communicate directly with all of the current holders of the >> capability. > > I would disagree with this. If the use of a capability without > authorization causes the requester to malfunction, then having a > protocol that doesn't begin revocation with notification can make the > authorization nearly worthless, as the actor using it can't be sure it > is safe. > > You also need to make sure that you don't have "transactions" that might > be corrupted with a revocation in their midst.
The problem with a "cooperative" approach is as you allude to below: how long do you wait for the "holder" to relinquish it? Do you wait in units of wall-clock time? (if so, how do you know the holder isn't blocked, preempted, etc and, as such, not even aware of your request through no fault of its own?) Even if you can be assured the holder has received your request and is currently executing, how long might it conceivably take for him to comply (in an orderly fashion)? So, as you acknowledge below, your app design must be able to handle this case -- which is essentially the asynchronous case. I currently manage *physical* resources asynchronously (though with notification after the fact) -- because they *can* disappear even without my explicit control (e.g., power failure, drop in water pressure, etc.). So, this same sort of reaasoning would at least be *consistent*. I.e., do an operation and *check* to see if it completed as expected (just like checking return value of malloc).
> Yes, sometimes just doing an asynchronous revocation may make sense, and > in many cases having it as a fall back if the cooperation method fails > to complete in a needed time is needed, but that doesn't mean that > asynchronous is generally preferred. > > As to the transitively granting, the same method could be used to relay > the request to revoke.
This is a tougher call (though I think I have a solution that addresses these issues). Who does the relaying? The actor who delegated the capability? (what if he is now a zombie?) Or, does the kernel track "derived capabilities" and treat them as part of the original capability? As I began my original post: "... i.e., how best to differentiate the examples where X should be allowed vs X should be prohibited." you can come up with examples where /each/ approach is "right" and the others *wrong*. :< Engineering: finding the least wrong solution to a problem. <frown> But, at least its interesting! :>
On 11/4/2013 8:05 PM, Don Y wrote:

> The problem with a "cooperative" approach is as you allude to below: > how long do you wait for the "holder" to relinquish it? Do you wait > in units of wall-clock time? (if so, how do you know the holder > isn't blocked, preempted, etc and, as such, not even aware of your > request through no fault of its own?) Even if you can be assured > the holder has received your request and is currently executing, how > long might it conceivably take for him to comply (in an orderly > fashion)? > > So, as you acknowledge below, your app design must be able to handle > this case -- which is essentially the asynchronous case. > > I currently manage *physical* resources asynchronously (though with > notification after the fact) -- because they *can* disappear even > without my explicit control (e.g., power failure, drop in water > pressure, etc.). So, this same sort of reaasoning would at least > be *consistent*. > > I.e., do an operation and *check* to see if it completed as expected > (just like checking return value of malloc). > >> Yes, sometimes just doing an asynchronous revocation may make sense, and >> in many cases having it as a fall back if the cooperation method fails >> to complete in a needed time is needed, but that doesn't mean that >> asynchronous is generally preferred. >> >> As to the transitively granting, the same method could be used to relay >> the request to revoke. > > This is a tougher call (though I think I have a solution that addresses > these issues). Who does the relaying? The actor who delegated > the capability? (what if he is now a zombie?) Or, does the kernel > track "derived capabilities" and treat them as part of the original > capability?
Come to think of it, any capability-based system in which the kernel does NOT maintain/implement the actual capabilities *must* rely on deferred errors that the holder(s) would have to detect and handle. E.g., In Amoeba and Chorus, the capability is just an integer (albeit one with magical properties). The kernel doesn't know how many "instances" of that number exist in the system! So, no way for it to notify all holders in advance of revocation or downgrading. Instead, the holder(s) eventually *use* the capability -- and find that "it doesn't work". I.e., if I change the permissions on a file after telling you the name of the file, you may not be able to access it as my initial intent might have suggested. So, if you *expect* the fopen() to succeed (and haven't coded for the !SUCCESS possibility), you've got a bug.
On 11/4/13, 10:05 PM, Don Y wrote:
> Hi Richard, > > On 11/4/2013 7:25 PM, Richard Damon wrote: >> On 11/4/13, 3:20 AM, George Neuner wrote: >>> On Fri, 01 Nov 2013 13:34:05 -0700, Don Y<this@isnotme.com> wrote: >>>> >>>> Some of the trickier issues I'm trying to address include: >>>> >>>> - "revoking" an authorization that I have previously given >>>> to another actor (do I do this asynchronously? synchronously >>>> with the other actor's consent/participation? etc.) >>> >>> Revoking always should be asynchronous because it is solely at the >>> discretion of the giver. If a capability can be delegated >>> transitively, the originating authority may neither know nor be able >>> to communicate directly with all of the current holders of the >>> capability. >> >> I would disagree with this. If the use of a capability without >> authorization causes the requester to malfunction, then having a >> protocol that doesn't begin revocation with notification can make the >> authorization nearly worthless, as the actor using it can't be sure it >> is safe. >> >> You also need to make sure that you don't have "transactions" that might >> be corrupted with a revocation in their midst. > > The problem with a "cooperative" approach is as you allude to below: > how long do you wait for the "holder" to relinquish it? Do you wait > in units of wall-clock time? (if so, how do you know the holder > isn't blocked, preempted, etc and, as such, not even aware of your > request through no fault of its own?) Even if you can be assured > the holder has received your request and is currently executing, how > long might it conceivably take for him to comply (in an orderly > fashion)? >
All questions to be decided at design phase, with no "generic answer". Presumably, if there is a deadline for when the acknowledgement can be given, then presumably this spec is applied when designing such a real time system.
> So, as you acknowledge below, your app design must be able to handle > this case -- which is essentially the asynchronous case. > > I currently manage *physical* resources asynchronously (though with > notification after the fact) -- because they *can* disappear even > without my explicit control (e.g., power failure, drop in water > pressure, etc.). So, this same sort of reaasoning would at least > be *consistent*. > > I.e., do an operation and *check* to see if it completed as expected > (just like checking return value of malloc). >
Some operations do not make checking at each operation so easy. What if the resource is access to some memory, do you check for an "error" after every access? This presumes that the system even gives you an application level ability to continue pass this sort of error. What do you do about cooperative "authorization" to access parts of structures for things like synchronization where there isn't a hardware/OS capability to stop you? In your case, since the operation do have the capability of suddenly starting to fail, an asynchronous revocation likely doesn't cause problems that you didn't need to handle anyway, as long as the system structure to allow it.
>> Yes, sometimes just doing an asynchronous revocation may make sense, and >> in many cases having it as a fall back if the cooperation method fails >> to complete in a needed time is needed, but that doesn't mean that >> asynchronous is generally preferred. >> >> As to the transitively granting, the same method could be used to relay >> the request to revoke. > > This is a tougher call (though I think I have a solution that addresses > these issues). Who does the relaying? The actor who delegated > the capability? (what if he is now a zombie?) Or, does the kernel > track "derived capabilities" and treat them as part of the original > capability? >
I would generally say that the actor who was given a permission is responsible for relaying the revocations to those it relayed to. If it has shared a right that it might have revoked from it, it needs to maintain a way to do that.
> As I began my original post: > "... i.e., how best to differentiate the examples where > X should be allowed vs X should be prohibited." > you can come up with examples where /each/ approach is "right" > and the others *wrong*. :< > > Engineering: finding the least wrong solution to a problem. > > <frown> But, at least its interesting! :>
This is why I object to the statement that it SHOULD ALWAYS be asynchronous. The only real answer is that "it depends", and lists can be made of what it depends on. Some examples include: If the authorization even remotely revokable? (Sometimes it isn't) What is the effect on the requesting task if the authorization goes away unexpectedly? What is the effect of delaying the revocation?

Memfault Beyond the Launch