[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Carl Fredrik Hammar
Thu, 23 Apr 2009 14:46:10 +0200
I guess this mail turned out to be more of a report on my findings rather
than to start a discussion. So I'm mostly looking for feedback, e.g. if
what I'm saying doesn't make sense, if one of my assumptions are wrong,
or if I have missed something. Anyways, here comes a long mail about
what I like to call ``authority verification''.
Though I am open to suggestions for alternatives to the name. ;-)
Most objects in the Hurd depend on other objects to function. In order
for a process to receive a mobile object from a sending process it must
gain access to those same, or equivalent, objects. Simply bundling
the dependencies with the mobile object could grant the receiver direct
access to objects it wouldn't otherwise be able to gain access to.
The sender is of course free to do this. However, that could effectively
punch a hole through the normal security setup. Overriding security in
this manner could be part of its functionality. But if that is not the
case, it would make users wary of loading mobile objects, making them
less effective overall.
However, if it's only used when the receiver already has the necessary
authority. There wouldn't be any new security concerns. Except perhaps
due to the increased complexity.
Also the sender shouldn't make assumptions based on the UIDs or GIDs
of the receiver, as its authority is based as much on context, i.e.
which capabilities it has. It is easy enough for the sender to send the
dependencies if the receiver is run by the same user or root. However,
for each such rule we make up, a security conscience user would have to
make sure it can't be exploited on a case-by-case basis. I believe we
should avoid constructing new access policies as much as possible.
The server that implements the dependency is also responsible for
implementing the access control for accessing its objects. Even if the
server advertises that it permits access, e.g. through file permission
bits, the fact remains that it may disallow access on other grounds.
For instance, there's nothing stopping a server from disallowing access to
according to an ACL, or during a certain time of day, or even completely
The only way to make /sure/ that the receiver has the necessary access
would be to request an equivalent object from the dependency server,
using only data and capabilities the receiver already has access to.
Note also that it's possible for the sender to trick the receiver to
use objects the sender itself does not have access to, in ways they
are not expected to be used. Possibly leading to precious data being
overwritten or sensitive data being transmitted. Thus we also need to
make sure the sender does indeed have the access it claims to have.
So we need to determine whether two objects are equal. And this test
should be done in a server trusted by both parties, similar to the auth
server, so the parties does not expose either port to one another.
If they are used through the same port, they are the same object and
therefore equal. However, most objects seem to be referenced indirectly
through session handles that remember the credentials of the client.
Not to mention the cursor state of normal file handles. Given this,
it is unlikely the server would be give out the same port twice.
Task ports and IO identity ports are exceptions to this. And I suspect
translator control ports and ports to devices implemented by Mach
might also be exceptions. Thought I'll need to look it up to be sure.
So comparing ports does have some uses.
Other than the port comparison, the Hurd offers no general mechanism
for testing object equality. The IO identity ports does offer a way to
test whether two handles uses the same underlying io object. However,
it says nothing on whether the handles are identical. The two handles
might permit different operations. Or like /dev/random send different
data to both parties, leaving the question whether they truly can be
considered equivalent (which happens to be the case for /dev/random).
The short of it is that existing Hurd servers are not equipped to deal
with the problem at hand. We could try reopening, using io_restrict_auth
on a duplicate, or some other trick. In the end, given the dynamic
nature of Hurd we can't be certain we end up with an equivalent object
in all cases.
The only way to make this work AFAICS, would require adding a equality
interface, or redesign interfaces to give out the same port for the same
objects. Both require changes to servers to make it work. The latter
would be a monumental task. The former can be added one server at a
time, but still I consider it to be out of the question for until libmob
It seems where out of luck when it comes to re-obtaining the dependencies
from their implementing servers. However, there is source the receiver
may be able to get the very same ports used by the sender: the sender
Its not uncommon that processes already has access to memory and ports
of another process, which is needed to debug a process. Using this,
the receiver could obtain all the necessary data and ports directly from
the sender. Even without cooperation, thought this would be trickier. ;-)
To do this the receiver must obtain the sender's task port through the
proc server. Note that the proc server and the task port it returns can
be proxied, both may block the necessary access, or perhaps even allow
access where the senders own proc server would not. So the receiver
should still go through all the steps to obtain ports and sensitive data
by itself, and not be given them in exchange for proof that it holds
the senders task port.
After the ports and data has been obtained, they should be tested for
equality. This ensures that the receiver has not been deceived by the
sender or a proc proxy.
A sketch of the steps of the process:
S sends its PID, port names, and memory addresses to R R gets PID's task
port from proc R reads ports and memory from task port S and R sends
ports and data to the equality server R gets answer from equality server
Note that this only works with send rights, since send-once and receive
rights can only be moved not copied. They would have to be handled
specially, though luckily they are likely not common as dependencies.
Another possibility which I have considered, is ports that are considered
safe for sending directly. For instance, libstore does this when a
store is opened read-write and the entire underlying device is used.
The reasoning being that accessing the store remotely still makes it
possible to access the entire store.
There are two flaws with doing this. First, the underlying device might
implement additional interfaces the store does not know about, in which
case it couldn't possibly know that it's safe to give out the port.
Second, it would no longer be possible to revoke the access to the
underlying device provided by the store by killing the store translator.
My next step will be to write up a prototype for transferring
dependencies. After that I'll consider problems related to specifying
an object's code base and loading it.
- Authority verification,
Carl Fredrik Hammar <=