Context
This is an application using Java RMI, with:
- A client on host A and a server on host B.
- A HTTP server on host A, it holds a copy of all classes used by the client and the server.
- The RMI registry is created from the server code, it shares server JVM and CLASSPATH.
For the server and the client applications:
- The
java.rmi.server.codebase
property is set (in the code) to the HTTP server URL. - A security manager is in place with the appropriate policies.
- The
java.rmi.server.hostname
is set to the LAN address.
Method invocation:
- A server method is defined with a parameter of type
WorkRequest
(abstract class). - The client invokes this method using a subclass
WorkRequestSquare
. WorkRequestSquare
is never mentioned on the server code
Server code
Object execute(WorkRequest work) throws RemoteException { return work.execute(); }
Client code:
try { servant.execute(new WorkRequestSquare(123)); }
catch (RemoteException e) {
System.out.println("Error while submitting work request:");
e.printStackTrace();
}
This code works when the server has all related classes in the CLASSPATH and no dynamic loading is necessary.
Problem
Removing WorkRequestSquare
from the CLASSPATH on the server side throws an exception on the client side (nothing is thrown on the server side):
Error while submitting work request:
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: WorkRequestSquare
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
...
WorkRequestSquare
being never explicitly mentioned on the server code, it should be dynamically loadable, but this doesn't happen.
Bypass
I was made aware by Stuart Marks that the property java.rmi.server.useCodebaseOnly
should be set to false
.
The default value for this property has been changed from false
to true
in JDK 7u21. useCodebaseOnly
set to true
prevents the JVM from dynamically loading classes from any location, except the one defined locally using java.rmi.server.codebase
. This means that the codebase value set by the client JVM when marshalling the WorkRequestSquare
parameter is ignored by a server which useCodebaseOnly
is true
.
When useCodebaseOnly
is set to false
on the server, and WorkRequestSquare
class is removed from the server CLASSPATH, the server now gets the class definition from the HTTP server. This is a valid bypass.
Question
There is still something abnormal as the client and the server have the same value for the codebase
property.
When useCodebaseOnly
was defaulted to true
the server JVM should have ignored client codebase
added to the RMI stream, but it should have anyway used its own codebase
to retrieve the WorkRequestSquare
class.
This should have been successful anyway because client and server codebase
values are identical.
Could someone cast some light on that matter?