9 3.3 Java RMI One major drawback of RPC is that it supports only a

advertisement
9
3.3 Java RMI
One major drawback of RPC is that it supports only a limited set of basic data types, and
cannot handle arbitrary objects to be sent to procedures as parameters or to be returned).
RMI was introduced to overcome this deficiency.
3.3.1 Interface definition
Java RMI does not need a special IDL, since it is used when both the client and the server
are written in Java. An application that makes its methods available to remote clients must
declare the methods in an interface that extends the java.rmi.Remote interface. 1 In the
rest of this section, we follow the example available at the Web page “Useful Links”/“Intro
to RMI” with some minor modifications. In “Useful Links”/“Getting Started Using RMI”,
the client is implemented as an applet, instead of an application, and therefore is more
complicated. You may want to read it after you understand what’s given below first.
Find.java file (contains an interface definition):
// Find.java
import java.rmi.*;
//Import the rmi package.
public interface Find extends Remote { //Must extend java.rmi.Remote.
public String findLine(String keyword) throws RemoteException;
}
//Each method must throw java.rmi.RemoteException.
3.3.2 Server implementation class
The method(s) declared in an interface must be implemented by the server and the server
object must be exported, i.e., made to begin listening for incoming RMI requests. Exporting
is implicit if the object extends the UnicastRemoteObject class in the java.rmi.server
package. In this case, the constructor exports the server object.2 This class also creates an
object that listens for network requests using a socket.
We now give an example of a server (FindImpl3 that finds if a query keyword appears
in a given text file containing many lines, and if so, prints out all the lines that contain the
query keyword). To provide this service, FindImpl first converts the given file into a Vector
by converting the lines of the file into vector elements. With this preparation, checking if
a given keyword appears in an element becomes an easy task, since such an operation is
provided by a Java API.
FindImpl.java file:
// FindImpl.java
import java.io.*;
import java.util.*;
import java.rmi.*;
import java.rmi.server.*;
1
An interface can be extended to another interface.
It can be exported explicitly by exportObject() of the java.rmi.server package.
3
It’s a common practice in the Java community to name the server implementing interface X as XImpl
2
CMPT401 Chapter 3, Summer 04
10
public class FindImpl extends UnicastRemoteObject implements Find {
private Vector list = new Vector();
public FindImpl(String aFile) throws RemoteException
//constructor
{
try {
FileReader fr = new FileReader(aFile);
BufferedReader br = new BufferedReader(fr);
String s = null;
//convert a line into a vector element
while ((s = br.readLine()) != null) list.addElement(s);
fr.close();
}
catch (Throwable e) {
System.err.println("exception");
System.exit(1);
}
}
//end of constructor
public String findLine(String keyword)
{
if (keyword == null) return null;
keyword = keyword.toLowerCase();
int n = list.size();
for (int i = 0; i < n; i++) { //For each line in the list
String line = (String)list.elementAt(i);
if (line.toLowerCase().indexOf(keyword) != -1) //Is "keyword" part of the l
return line;
//Return the line containing "keyword"
}
return null;
}
public static void main(String args[])
{
try {
RMISecurityManager security = new RMISecurityManager();
System.setSecurityManager(security);
String aFile= args[0];
FindImpl server = new FindImpl(aFile);
Naming.rebind("//127.0.0.1:2099/FindServer", server);
System.out.println("FindServer ready...");
}
catch (Throwable e) {
11
CMPT401 Chapter 3, Summer 04
System.err.println("exception: " + e);
System.exit(1);
}
}
}
FindServer is any string that you choose as the name FindImpl is to be known
by. server is an FindImpl object which has the server port number encoded in it.
Naming.rebind sends the codebase information to rmiregistry.
RMISecurityManager enforces security restrictions on the classes that are downloaded
from the network. See §3.4.
3.3.3 Registry
We assume here that the RMI registry service is available at port 2099 of the server
host. Thus we bind FindImpl to this registry service. If you omit //127.0.0.1:2099 in the
above example, the default is "//127.0.0.1:1099", where 1099 is the well-known address
of the RMI registry service.
Note: As the port designation of the rmiregistry, instead of //127.0.0.1:2099, you can
also use //:2099, or rmi://orion.csil.sfu.ca:2099, etc., assuming you are running the
RMI registry (and the server) on orion.
2
A simple way of starting an RMI registry is to use the rmiregistry application that
comes with the Java SDK. In Unix, just type the command
rmiregistry 2099 &
and in Windows, type
start rmiregistry 2099
Registry Server
1. rmiregistry &
5. Return reference
Client Application
4. Lookup by name
2. Export service
3. Bind to Registry
8.RMI
6.
Server Application
HTTP Server
7.
Stub
Sleketon
Machine A
Machine B
Figure 5: RMI steps
on the host (on which the server will be running), where 2099 is the port number used
by the registry server. A server registers its service with a registry whose URL is either
well-known or can be known by some means. To register service, the server calls methods,
CMPT401 Chapter 3, Summer 04
12
bind or rebind, of the rmi.Naming class, e.g., rmi.Naming.rebind. 4 To find the object
corresponding to a service name, a client uses rmi.Naming.lookup, which is URL-formatted.
See the client example in the next subsection. The client can then make method calls to the
object returned by rmi.Naming.lookup. (The client stub gets in action behind the scenes.)
3.3.4 Client
FindClient.java file:5
// FindClient.java
import java.rmi.*;
import java.rmi.server.*;
public class FindClient {
public static void main(String args[])
{
try {
RMISecurityManager security = new RMISecurityManager();
System.setSecurityManager(security);
//Assuming the server is running on orion
String name = "rmi://" + "orion.csil.sfu.ca:2099" + "/FindServer";
Find ref = (Find)Naming.lookup(name);
String results = ref.findLine(args[0]);
if (results == null)
System.err.println("** not found **");
else
System.out.println(results);
}
catch (Throwable e) {
System.err.println("exception: " + e);
System.exit(1);
}
}
}
3.3.5 Compiling and Running
For details, read FindService.pdf available in the Assignment menu.
Server is started with
java -Djava.rmi.server.codebase=http://orion.scil.sfu.ca/∼tiko/codebase/
FindImpl
4
bind may throw AlreadyBoundException, while rebind quietly replaces the existing object with the
same name as the object being bound, if any.
5
In the following code, ref is cast as interface Find, not as FindImpl, which might appear more logical.
Note, however, that FindImpl.class is not normally available in the client machine.
13
CMPT401 Chapter 3, Summer 04
Before starting rmiregistry, make sure that CLASSPATH (local codebase) on the server
machine does not contain the path to the client stub. Otherwise, rmiregistry will not pass
java.rmi.server.codebase property to client (and client will not be able to download the
stub). Read the rmi tutorial on the Sun Web site.
• Compile: javac FindImpl.java generates FindImpl.class.
• Compile: javac FindClient.java generates FindClient.class.
• Run the stub generator:
FindImpl Stub.class.
rmic FindImpl generates FindImpl Skel.class and
• Move FindImpl Stub.class and to codebase, e.g., ∼tiko/codebase.
To run the programs, follows the steps below:
• Start Registry: rmiregistry 2099 & (Unix), or start rmiregistry 2099 (Windows).
• Run server and register
java -Djava.rmi.server.codebase=http://serverHost/∼tiko/codebase/
FindImpl phonebook
where phonebook is a text file containing a name and his/her phone number on each
line.
• Move FindClient.class and Find.class to the client machine (may be on another
network) unless it was compiled there.
Run Client: java FindClient smith
3.4 Java Security
In Java, executable contents, such as applets and stubs, can be shipped to other
hosts and executed. This has serious implications for security. At the client site, an applet
may use classes from the following three groups of classes:
1. The base classes (i.e., java’s built-in classes, e.g., java.lang.*),
2. local classes (on CLASSPATH), or
3. remote classes (remote for the standpoint of the client).
The first two are trusted classes and the third is untrusted. An untrusted (and possibly
malicious) executable object downloaded from the Internet should be run in a restricted
environment (e.g., sandbox). The default is that such an object is not allowed to access
the local file system, except for loading trusted classes that are locally available (1. and 2.
14
CMPT401 Chapter 3, Summer 04
02/06/2003
4
Figure 6: Protection domains
in the above list). By default, it can access only data/programs from the remote machine
where it came from.6
Recent Java versions (1.2 or newer) have more flexibility and fine-grained control regarding security. (See Textbook p. 256.) As shown in Fig. , loaded code can be put in different
protection domains, depending on the trust the client has in it. A protection domain
contains codes from the same source (URL) and with the same signature. The system code
shipped with the JDK (see group 1 above) is in a unique domain. The most trustworthy
are allowed to access any system resources (see the top left of the figure), while the least
trustworthy are put in a sandbox with the default restrictions stated above. There is a single
system-wide policy file, which applies to every user, and a single user policy file for each
user. The system policy file is by default located at
java.home/lib/security/java.policy (Solaris) 7
java.home\lib\security\java.policy (Windows)
The system policy file grants system-wide code permissions. The java.policy file installed with the JDK grants all permissions to standard extensions, allows anyone to listen
on un-privileged ports, and allows any code to read certain “standard” properties that are
not security-sensitive, such as the os.name and file.separator properties. Here is a part
of the java.policy file:
// Standard extensions get all permissions by default
grant codeBase "file://${java.home}/lib/ext/*" {
permission java.security.AllPermission;
6
It is specified as the codebase in the <APPLET> tag in the HTML file. The default for codebase= is
the directory where the HTML file came from.
7
java.home refers to the value of the system property named java.home, which specifies the directory
into which the JDK was installed.
CMPT401 Chapter 3, Summer 04
15
};
grant {
// Allows any thread to stop itself using java.lang.Thread.stop().
// Note that this permission is granted by default only to remain
// backwards compatible.
// It is strongly recommended that you either remove this permission
// from this policy file or further restrict it to code sources
// that you specify, because Thread.stop() is potentially unsafe.
// See "http://java.sun.com/notes" for more information.
permission java.lang.RuntimePermission "stopThread";
// allows anyone to listen on un-privileged ports
permission java.net.SocketPermission "localhost:1024-", "listen";
// "standard" properties that can be read by anyone
permission java.util.PropertyPermission "java.version", "read";
permission java.util.PropertyPermission "java.vendor.url", "read";
permission java.util.PropertyPermission "os.name", "read";
permission java.util.PropertyPermission "os.version", "read";
permission java.util.PropertyPermission "os.arch", "read";
...
}
The user policy file is by default located at8
user.home/.java.policy (Solaris)
user.home\.java.policy (Windows)9
When the Policy is initialized, the system policy is loaded in first, and then the user policy
is added to it. If neither policy is present, a built-in policy is used. This built-in policy is
the same as the sandbox policy given earlier in this section.
In JDK 1.2 RMI, you need to specify a policy file when you run your server and client.
Given below is a general policy file that allows downloaded code, from any code base, to do
the following two things:
• Connect to or accept connections on unprivileged ports (≥ 1024) on any host
• Connect to port 80 (HTTP port) on any host
8
Policy file locations are specified in the security properties file, which is located at
java.home/lib/security/java.security (Solaris) or java.home\lib\security\java.security (Windows). A policy file can be placed in the current directory as well.
9
user.home refers to the value of the system property named user.home, which specifies the user’s home
directory. Given user name uName, the user.home property value defaults to
C:\Winnt\Profiles\uName on multi-user Windows NT systems
CMPT401 Chapter 3, Summer 04
16
grant {
permission java.net.SocketPermission "*:1024-", "connect,accept";
permission java.net.SocketPermission "*:80", "connect";
};
FilePermission
http://java.sun.com/products/jdk/1.2/docs/guide/security/permissions.html
The following sample policy file entry grants code from the /home/sysadmin directory
read access to the file /tmp/aFile:
grant codeBase "file:/home/sysadmin/*" {
permission java.io.FilePermission "/tmp/aFile", "read";
};
A java.io.FilePermission represents access to a file or directory. 10 It is followed by
a pathname and a set of actions permitted for that pathname. A pathname consisting of
a single “*” indicates all the files in the specified directory, while a pathname consisting of
a single “-” indicates all the files in the specified directory and (recursively) all files and
subdirectories contained in the specified directory.
The actions to be granted are passed to the constructor in a string containing a list of
zero or more comma-separated keywords. The possible keywords are “read”, “write” (which
includes permission to create) “execute” (allows Runtime.exec to be called and checked by
SecurityManager.checkExec), and “delete” (checked by SecurityManager.checkDelete).
grant {
permission java.io.FilePermission
"c:\\home\\ann\\public_html\\classes\\-", "read";
permission java.io.FilePermission
"c:\\home\\jones\\public_html\\classes\\-", "read";
};
Note that in Windows-style file names, the backslash character needs to be represented
by two backslash characters in the policy file.
In addition to giving different privileges based on the origin of the code, codes signed by
particular owners can be treated preferentially using “grant signedBy”.
Policy Enforcement by Security Manager
SecurityManager is an object of class11 java.lang.SecurityManager. Various methods in the Java libraries call a check method before performing an operation which could
potentially affect the system integrity (e.g., file read/write, network access, access to the
environmental variables) A Security Manager, if one is installed, checks if the operation
10
Code can always read a file from the same directory it’s in (or a subdirectory of that directory); it does
not need explicit permission to do so.
11
It was an abstract class in JDK1.1.
CMPT401 Chapter 3, Summer 04
17
is permitted, and if so simply returns; otherwise, it throws a SecurityException. Since
access to these key system functions is controlled by calls within the trusted classes, there
is no way to avoid this validation.
A browser installs AppletSecurityManager as part of the JVM initialization, and once
installed, an applet cannot change it (e.g., to expand its privilege). AppletSecurityManager
ensures that an applet downloaded over a network12 is subjected to the sandbox policy, i.e.,
it cannot read/write in the local file system, start other programs on the client host, or make
network connections to other sites, except to its originating host. The aim is to prevent an
applet from doing the following:
1. Uninvited retrieval of data by reading local files (other than those reachable via
CLASSPATH) or extracting local environmental info.
For example, a checkRead method of a Security Manager receives a file reference
as an argument. A security exception is thrown when an applet attempts to read
a file it is not allowed to. Similarly for checkWrite. When an applet invokes
Socket(host,port), the constructor method of the Socket class in turn invokes the
checkConnect(host,port) method of the Security Manager, which compares host
with the source where the applet came from. If the applet is untrusted, host must
agree with its origin.13 Otherwise, a security exception is thrown.
2. Overriding trusted built-in classes on the client machine.
3. Using the client machine as a platform to attack other systems.
4. Changing the environment by making calls, e.g., System.exit() in an attempt to kill
the browser it is executing in. (checkExit() of AppletSecurityManager throws an
exception.)
All JDK system code invokes SecurityManager methods (e.g., checkRead) to check
the policy currently in effect and perform access control checks. Such a method throws
an AccessException when a prohibited method is called. Here are some examples of
RMISecurityManager methods:
• public synchronized void checkAccept(String host, int port): Does not permit a stub to accept socket connections.
• public synchronized void checkConnect(String host, int port):
permit a stub to make socket connections unless it is loaded locally.
Does not
• public synchronized void checkDelete(String file): Does not permit a stub to
delete files.
12
An applet stored in the local disk and is on the CLASSPATH of the program that runs it (e.g.,
appletviewer), then it is loaded by the file system loader, and can access the local file system. If it is
loaded by means of an appletloader by a browser, it is subjected to the same restrictions as downloaded
applets.
13
Exactly the same name that was used to download the applet must be used to specify this originating
host. Using the equivalent IP address, for example, won’t work.
CMPT401 Chapter 3, Summer 04
18
• public synchronized void checkExec(String command): Does not permit a stub
to execute code.
• public synchronized void checkExit(int StatusCode): Does not permit a stub
to kill the Virtual Machine.
• public synchronized void checkListen(int port): Does not permit a stub to
listen to a port.
• public synchronized void checkRead(String file): Does not permit a stub to
read a file.
• public synchronized void checkWrite(String file): Does not permit a stub to
write to a file.
Typically, a SecurityManager is installed whenever an applet is running; i.e., the appletviewer and most browsers install a security manager. A security manager is not automatically installed when an application is started. To apply the same security policy to an
application found on the local file system as to downloaded applets, you can use one of the
following two methods:
1. By setting a system property (java.security.manager property) in the command
line when lauching the JVM.
java -Djava.security.manager myApp.
2. From within your program.
System.setSecurityManager (new SecurityManager()).
In the first method, it is possible to specify a particular security manager to be utilized,
as in
java -Djava.security.manager=MySecMgr myApp
All of the following are equivalent and result in usage of the default security manager:
java -Djava.security.manager myApp
java -Djava.security.manager="" myApp
java -Djava.security.manager=default myApp
JDK 1.2 includes a property named java.class.path. Classes that are stored on the
local file system but should not be treated as base classes should be on this path. Classes on
this path are loaded with a secure class loader and are thus subjected to the security policy
being enforced. There is also a command-line argument whose usage determines what policy
files are to be enforced. If you don’t include -Djava.security.policy on the command
line, then the policy files specified in the security properties file will be used. For example, if
you type the following, where pURL is a URL specifying the location of a policy file, then the
specified policy file will be loaded in addition to all the policy files specified in the security
properties file:
CMPT401 Chapter 3, Summer 04
19
java -Djava.security.manager -Djava.security.policy=pURL myApp
If you use a double equals (==) instead of =, then just the specified policy file will be
used; all others will be ignored:
The RMI runtime requires that a security manager be explicitly set before any remote
stub classes can be downloaded over the network. RMISecurityManager allows the stub
classes to download necessary class files (from their codebase) over the network.
Notes: See http://java.sun.com/docs/books/tutorial/rmi/running.html
Class loaders
Class loaders are the gate keepers of the JVM, controlling what byte code may be loaded.
Each class loader itself must be loaded by another class loader. The chicken-and egg problem
is overcome by the initial loader, called the primordial class loader, which is written in
a native language (platform-dependent) and is available when the JVM is started. All other
class loaders are a subclass of an abstract Java built-in class, java.lang.ClassLoader. It
defines the loadClass() method, which reads in class data and calls the defineClass()
method to create an instance of the class Class. java.lang.ClassLoader has been extended
by a subclass, java.security.SecureClassLoader.
There are different loaders for different types of applications. The primordial class loader
loads all classes that are part of the Java API, while URLClassLoader loads classes from the
CLASSPATH.
Since applets have special security needs, an AppletClassLoader, which is a subclass
of java.security.SecureClassLoader, is used for them. It dynamically load (locally or
remotely, as needed) the classes that the applet extends or the classes which the applet creates
instances of. Each applet is loaded by a different AppletClassLoader. If a downloaded
applet creates an instance of some class, then the AppletClassLoader first delegates loading
to primordial class loader. If it is not locally available via the CLASSPATH environment
variable, the AppletClassLoader loads it from the site where the applet came from. 14 The
search order is always: (1) Java built-in classes, (2) local classes (in the current directory or
reachable via CLASSPATH), and then (3) remote classes. This order is not just for convenience.
Assume that someone at a remote site wrote a malicious class, which has the same name as
one of the built-in classes, e.g., SecurityManager(), and made it available for downloading.
If (3) is searched before (1), this malicious class would be loaded as the security manager!
The primary security function of class loaders is to prevent classes loaded from untrusted
sources from being confused with trusted classes.
RMIClassLoader takes care of loading for RMI. In particular, it loads stub classes,
as well as any utility classes needed by these stub classes. As for the stub classes, the
java.rmi.server.codebase property is used to locate them. As described in the write-up
FindService.pdf in the Assignment menu, this can be set by the −D flag when the Java
interpreter is launched to run the server.
java -Djava.rmi.server.codebase=http://orion.csil.sfu.ca:2001/ FindImpl
phonebook.txt
14
ClassNotFound exception is thrown, if not remotely available either.
CMPT401 Chapter 3, Summer 04
20
The server passes this information to rmiregistry, which annotates the reference to the
server by its codebase.
As for objects passed as parameters or return types, RMIClassLoader first attempts
to load classes on the local file system via CLASSPATH just like AppletClassLoader. If it
fails, then it extracts the URL annotating the serialized objects, which is then used as the
codebase.
If
you
want
to
force
RMI
to
only
load
classes
locally,
the java.rmi.server.useCodebaseOnly property can be set. In this case, if the needed
class is not available locally, a ClassNotFound exception is thrown.
Class file verifier
Verifies that the downloaded untrusted class files obey the rules of JVM (Jave Virtual
Machine) by carrying out the following:
1. File integrity check: makes sure that the length of each structure in the file matches
the specs in the header.
2. Class integrity check: e.g., the class has a superclass, unless it is of type Object, the
superclass is not a final class (which cannot be extended), and it does not override a
final method in its superclass, etc.
3. Bytecode Integrity Check: Bytecode generated by the correct Java compiler will be
correct, but bytecode can be handcrafted, generated by a “hostile compiler”, or even
compiled from a non-Java language. So it could be malicious, and may not obey the
language rules. Checks are made for:
(a) Illegal bytecode instructions, which will confuse JVM.
(b) Incorrect number and types of operands, illegal access to classes, fields (data) or
methods.
(c) Over(under)-flowing the program stack. Simulated execution is carried out using
only the type info (not the actual values) of the items pushed into or popped out
of the stack.
(d) Illegal casting.
From http://java.sun.com/sfaq/#appletCL:
The verifier ensures that
• There are no stack overflows or underflows.
• All register accesses and stores are valid.
• The parameters to all bytecode instructions are correct.
• There is no illegal data conversion.
The verifier accomplishes that by doing a data-flow analysis of the bytecode instruction
stream, along with checking the class file format, object signatures, and special analysis of
finally clauses that are used for Java exception handling.
Download