Hi, 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 at random. 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 has stabilized. 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 itself. 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. Regards, Fredrik
