Sponsored Link •
Bob Scheifler talks with Bill Venners about the mechanism of determining whether a Jini proxy should be trusted.
Many potential applications of Jini require network security. Although various third parties have made proprietary security extensions to Jini, until now the only security available to users of the standard Jini release is the security infrastructure of the Java platform. The Jini Community's Davis project is about to change that. Bob Scheifler is leading the development of the next release of Jini, in which security is the central concern, as part of the Davis project.
On Friday, April 12, 2002 Bill Venners visited the Sun Microsystems campus in Burlington, Massachusettes and interviewed Bob Scheifler, Sun Distinguished Engineer and architect in the Jini Group. In Part I of this interview, Scheifler discusses the need for security in Jini and the special security considerations of dynamically downloaded code. In this second installment of the interview, Scheifler discusses the mechanisms used to determine whether a proxy should be trusted.
Bill Venners: You mentioned that the three special security ramifications of downloaded code are mutual authorization, proxy trust, and object integrity. Why don't we go through these three aspects in more depth, starting with the second one you mentioned. How do I decide whether to trust a proxy?
Bob Scheifler: OK. Assume I have downloaded a service proxy from somewhere. I think it is proxy for some particular service that I have in mind, and now I must decide if I should trust it. The mechanism we have in place right now has two phases. One is that I can simply do a depth-first analysis of the proxy object.
Bob Scheifler: I can say, "Here is the object. What is its class? Is that a downloaded class or a local class relative to me?" If it is a local class, then I can decide whether I trust that class. You might decide all local classes are trustworthy, or maybe you have particular classes that you trust for these purposes. Nonetheless I can look at it, and if it is a local class that I understand I can decide whether or not I trust it.
I can then recursively look at the classes of all of the fields of the object. I can reflectively pull the object apart and do a graph walk of the complete proxy object and all of its subsidiary objects. I can determine if all of their classes are local. If they are local and the data looks good for those types of objects, then I might decide, OK, sure I trust this.
In effect, I have proven to myself that there is no downloaded code. So then I am back to the traditional case. I use local code I trust. There is no downloaded code involved. It might have looked like a downloaded object but, in fact, all of the code resolved locally. I can trust that because it is all local code. I know exactly what it will do. I just call through it.
That is the first stage. It is an interesting mechanism, but it doesn't actually deal with any downloaded code. So how do we deal with the downloaded code case? It sounds a little funny, but I will ask the service if the service trusts the proxy.
Bob Scheifler: I think I have a proxy for a server run by Bill Venners. What I really want to do is ask Bill Venners if he trusts the proxy. The thinking is, I trust Bill. If I talk to Bill as the service anyway, I have to trust Bill, right? If I trust Bill and Bill trusts the proxy, then I should trust the proxy. At least I should trust the proxy to the extent that I trust Bill to perform whatever service I ask him to perform.
So I want to turn the question around. Instead of deciding whether I trust the proxy, I will ask if the service trusts the proxy. Now that sounds good in theory, but there's a flaw. How do I ask the service if it trusts the proxy if the only way I can talk to the service is through a proxy I don't trust?
Bob Scheifler: There is no magical answer to that. In the mechanism we defined, you bootstrap up. You might equate bootstrapping with cheating, but it is a way of getting started. To bootstrap up, I ask, "Do you trust the proxy?" I require that you give me a proxy that has no downloaded code.
I ask the service proxy, which I don't yet trust, for a bootstrap proxy. It gives me a new proxy back, the bootstrap proxy. The bootstrap proxy doesn't implement all of the interfaces that talk to the service. It just implements one interface that allows me to ask the question, "Does the service trust the proxy?" Before I trust the bootstrap proxy, however, I do an object graph to determine if the bootstrap proxy is, in fact, all local code. In this manner I can decide that I trust the bootstrap proxy at least for the purpose of asking you this one question.
Once I have decided I trust the bootstrap proxy, I make a call through it. I use code that I trust, and as part of that call I say, "Make sure when I ask this question that the server authenticates as Bill." Now I know I am really asking Bill the question and not somebody else. Through the trusted bootstrap proxy, I ask you to give me a verifier object I can use to test whether a proxy in my address space is trusted by you for your service. I don't send a proxy to you for you to verify because that has other problems. Instead, I ask you to send me a verifier I will run in my local address space.
Bob Scheifler: Now I have to worry whether I can trust the verifier I get from you. I must make sure some third party can't corrupt the verifier when you send it to me. This is where the object integrity mechanisms come into play. I must use object integrity in the process of making this one call to you to ask you for a verifier. I must make sure that both the data for the verifier you send me and any code I might download is actually the data and code you sent.
As part of this call therefore, I require you to authenticate and require that the communication have full object integrity. This way I know that the verifier I get back is, in fact, the verifier you intended to send. As a result of that call, then, I have a verifier object I know I can trust. I know I got the verifier object from you, and I know I got it intact. Now I can pass the suspect proxy object to the verifier and ask, "Verifier, do you trust this proxy object?"
The verifier can check code and data. It can leave pieces out if they are irrelevant. The verifier can do the necessary checking to decide that the suspect proxy is in fact a trustworthy proxy for you the service implementer.
Then the chain is complete. I know I have talked to you. I know you sent me a verifier I can trust. And I know you said you trust the proxy. Since I trust you for the purposes of performing operations on the service, then I should trust you to the same degree when you say that I should trust the proxy.
Bill Venners: Why do you use a verifier object rather than pass the suspect proxy directly to the bootstrap proxy?
Bob Scheifler: I don't send the proxy to you because of the way marshaling works. If I just simply ask this evil object to serialize itself, it could serialize itself into a form that is perfectly reasonable on your side but that would still be evil on my side. I have to make sure I really verify the object I will use, not some arbitrary representation of it. So the verification has to take place in my VM.
Bill Venners: What kinds of things might a verifier do to check the suspect proxy?
Bob Scheifler: That depends on the complexity your object. Let's start with a simple
case. You hand out the same immutable proxy object to everybody. It
has a code base that's a JAR file. A simple verifier contains a copy of
the immutable proxy object and does an equality check against the
suspect proxy with
.equals. You call
.equals on the expected proxy, the one contained inside
the verifier, passing it the suspect proxy. You write
.equals so that it recursively checks for data and class
equality on all the objects in the graph, and you're done.
Bill Venners: Earlier you said, "local relative to me." What do you mean by that?
Bob Scheifler: Suppose I am an activatable service trying to use some other service. An activation descriptor specifies a code base from which my code will load into a virtual machine in the activation group. Because I am dynamically loaded into a virtual machine, I am in some sense not local. "Local to me" just simply means classes loaded by my class loader and its ancestor class loaders. The fact that I am myself dynamically downloaded to somebody else doesn't really matter. What matters is that I have a firm notion of whether something is foreign to me or not, regardless of whether I may be foreign to the next guy.
Bill Venners: To what extent is the proxy trust algorithm configurable?
Bob Scheifler: We have designed the system so you can configure the proxy trust algorithm. If you want to configure in another algorithm, or if you want to deconfigure the default one, you can. For example, though it is not the Jini way, if you're paranoid you might configure a system to not trust any downloaded code. You could simply configure everything to say, "If it is not local code, don't use it."
Somebody might come up with some new algorithms. As new algorithms come into play, we wanted some level of API indirection so that application code wasn't coded directly to particular algorithms but indirectly to a set of algorithms. And those algorithms can be picked independently.
Bill Venners: Which part of the proxy trust verification mechanism is pluggable?
Bob Scheifler: The whole algorithm. The client will decide whether to trust a proxy. It calls into a static method to say please verify that this proxy should be trusted. Underneath the covers, it will look at some configuration setup to decide which algorithms to deploy to decide if the proxy should be trusted. If I come up with new algorithms I don't have to change my code. I have to change how it is deployed.
Bill Venners: In your JavaOne talk, you mentioned you wanted a client to only know what server it wanted to talk to, but that you couldn't quite get there. Can you clarify?
Bob Scheifler: I have a proxy I don't quite trust, and I want to decide whether I should trust it. What information do I think I already know that I might use as a basis to decide whether or not I trust this suspicious proxy? The one piece of information I think I know is who you the server are. I think this is a service that should be run by Bill Venners. Presumably that is the one piece of information I know. Server identity is the only thing I really need to rely on in this whole proxy trust verification mechanism. If you think about the bootstrapping mechanism, I ended up with one additional assumption. I assume we can both agree on what forms of boot proxies will be mutually trusted.
Bill Venners: Because I as a server provider have to send you the boot proxy.
Bob Scheifler: You have to send me one of the boot proxies that I have code for. And I not only have to have code for it, but it has to be code that I trust for this purpose. That is where we didn't quite meet the goal. We have to mutually agree for interoperability purposes on what we consider a trusted boot proxy. And it is only for that bootstrapped negotiation. It doesn't mean you have to use the same code or algorithms or protocols for the rest of the proxy's communication with the service. This bootstrap proxy is only used for one purpose, which is to ask it for this verifier. After that we throw it away. You are not required to use that same proxy for the rest of your communications between your proxy and your service. The normal service proxy still has the same kinds of implementation freedoms it had before.
The Jini Community, the central site for signers of the Jini Sun Community Source Licence to interact:
To get involved in the Davis project, in which Jini security is being defined, go to: