100.

previous chapter table of contents next chapter
  

Being Paranoiac

Jini applications download and execute code from other sources:

  • Both clients and services download ServiceRegistrar objects from lookup services. They then call methods such as lookup() and register() .
  • A client will download services and execute whatever methods are defined in the interface.
  • A remote listener will call the notify() method of foreign code.

In a safe environment where all code can be trusted, no safeguards need to be employed. However, most environments carry some kind of risk from hostile agents . An attack will consist of a hostile agent implementing one of the known interfaces (of ServiceRegistrar , of a well-known service such as the transaction manager, or of RemoteEventListener ) with code that does not implement the implied "contract" of the interface but instead tries to perform malicious acts. These acts may not even be deliberately hostile; most programmers make at least some errors, and these errors may result in risky behavior.

There are all sorts of malicious acts that can be performed. Hostile code can simply terminate the application, but the code can perform actions such as read sensitive files, alter sensitive files, forge messages to other applications, perform denial of service attacks such as filling the screen with useless windows , and so on.

It doesn't take much reading about security issues to instill a strong sense of paranoia, and possible overreaction to security threats. If you can trust everyone on your local network (which you are already doing if you run a number of common network services such as NFS), then the techniques discussed in this section are probably overkill. If you can't, then paranoia may be a good frame of mind to be in!

Protection Domains

The Java 1.2 security model is based on the traditional idea of "protection domains." In Java, a protection domain is associated with classes based on their CodeSource , which consists of the URL from which the class file was loaded (the codebase ), plus a set of digital certificates used to sign the class files. For example, the class files for the LookupLocator class are in the file jini- core .jar (in the lib directory of the Jini distribution). This class has a protection domain associated with the CodeSource for jini-core.jar . (All of the classes in jini-core.jar will belong to this same protection domain.)

Information about protection domains and code sources can be found by code such as this, which can be placed anywhere after the registrar object is found:

 java.security.ProtectionDomain domain = registrar.                                             getClass().getProtectionDomain(); java.security.CodeSource codeSource = domain.getCodeSource(); 

Information about the digital signatures attached to code can be found by code like this, which can also be placed anywhere after the registrar object is found:

 Object [] signers = registrar.getClass().getSigners(); if (signers == null) {     System.out.println("No signers"); } else {     System.out.println("Signers");     for (int m = 0; m < signers.length; m++)         System.out.println(signers[m].toString()); } 

By default, no class files or jar files have digital signatures attached. Digital signatures can be created using keytool (part of the standard Java distribution). These signatures are stored in a keystore. From there, they can be used to sign classes and jar files using jarsigner , exported to other keystores, and generally be spread around. Certificates don't mean anything unless you believe that they really do guarantee that they refer to the "real" person, and certificate authorities,such as VeriSign, provide validation techniques for this.

This description has been horribly brief and is mainly intended as a reminder for those who already understand this stuff. If you want to experiment, you can do as I did and just create certificates as needed, using keytool , although there was no independent authority to verify them. A good explanation of this topic is given by Bill Venners at http://www.artima.com/insidejvm/ed2/ch03Security1.html .

Signing Standard Files

None of the Java files in the standard distribution are signed. None of the files in the Jini distribution are signed either. For most of these it probably won't matter, since they are local files.

However, all of the Jini jar files ending in -dl.jar are downloaded to clients and services across the network and are Sun implementations of "well-known" interfaces. For example, the ServiceRegistrar object that you get from the discovery process (described in Chapter 3) has its class files defined in reggie-dl.jar , as a com.sun.jini.reggie.RegistrarImpl_Stub object. Hostile code implementing the ServiceRegistrar interface can be written quite easily. If there is the possibility that hostile versions of lookup services (or other Sun-supplied services) may be set running on your network, then you should only accept implementations of ServiceRegistrar if they are signed by an authority you trust.

Signing Other Services

Interfaces to services such as printers will eventually be decided upon, and will become "well known." There should be no need to sign these interface files for security reasons, but an authority may wish to sign them for, say, copyright reasons. Any implementations of these interfaces are a different matter. Just like the cases above, these implementation class files will come to client machines from other machines on the local or even remote networks. These are the files that can have malicious implementations. If this is a possibility, you should only accept implementations of the interfaces if they are signed by an authority you trust.

Permissions

Permissions are granted to protection domains based on their codesource, which consists of the codebase and a set of digital signatures. In the Sun implementation, this is done in the policy files, by grant blocks:

 grant codeBase "url" signedBy "signer" {     ... } 

When code executes, it belongs to the protection domains of all classes on the call stack above it. So, for example, when the ServiceRegistration object in the complete.FileClassifierServer is executing the register() method, the following classes are on the call stack:

  • The com.sun.jini.reggie.RegistrarImpl_Stub class from reggie-dl.jar
  • The complete.FileClassifierServer class, from the call discovered ()
  • Core Jini classes that have called the discovered() method
  • Classes from the Java system core that are running the application

The permissions for executing code are generally the intersection of all the permissions of the protection domains it is running in. Classes in the Java system core grant all permissions, but if you restrict the permissions granted to your own application code to core Jini classes, or to code that comes across the network, you restrict what an executing method can do.

For example, if multicast request permission is not granted to the Jini core classes, then discovery cannot take place. This permission needs to be granted to the application code and also to the Jini core classes.

It may not be immediately apparent which protection domains are active at any point. For example, in the earlier call of

 registrar.getClass().getProtectionDomain() 

I fell into the assumption that the reggie-dl.jar domain was active because the method was called on the registrar object. But it wasn't. While the getClass() call is made on the registrar , this completes and returns a Class object so that the call is made on this object, which by then is just running in the three domains: the system, the application, and the core Jini classes domains, but not the reggie-dl.jar domain.

There are two exceptions to the intersection rule. The first is that the RMI security manager grants SocketPermission to connect back to the codebase host for remote classes. The second is that methods may call the AccessController.doPrivileged() method. This essentially prunes the class call stack, discarding all classes below this one for the duration of the call, and it is done to allow permissions based on this class's methods, even though the permissions may not be granted by classes earlier in the call chain. This allows some methods to continue to work even though the application has not granted the permission, and it means that the application does not have to generally grant permissions required only by a small subset of code.

For example, the Socket class needs access to file permissions in order to allow methods such as getOutputStream() to function. By using doPrivileged() , the class can limit the "security breakout " to particular methods in a controlled manner. If you are running with security access debugging turned on, this explains how a large number of accesses are granted even though the application has not given many of the permissions.

Putting It Together

Adding all the bits of information presented in this chapter together leads to security policy files that restrict possible attacks:

  1. Grant permissions to application code based on the codesource. If you suspect these classes could be tampered with, sign them as well.
  2. Grant permission to Jini core classes based on the codesource. These may be signed if need be.
  3. Grant permission to downloaded code only if it is signed by an authority you trust. Even then, grant only the minimum permission needed to perform the service's task.
  4. Don't grant any other permissions to other code.

A policy file based on these principles might look like this:

 keystore "file:/home/jan/.keystore", "JKS"; // Permissions for downloaded classes grant signedBy "Jan" {     permission java.net.SocketPermission "137.92.11.117:1024-",                                          "connect,accept,resolve"; }; // Permissions for the Jini classes grant codeBase "file:/home/jan/tmpdir/jini1_1/lib/-" signedBy "Jini" {     // The Jini classes shouldn't require more than these     permission java.util.PropertyPermission "net.jini.discovery.*", "read";     permission net.jini.discovery.DiscoveryPermission "*";     // multicast request address     permission java.net.SocketPermission "224.0.1.85", "connect,accept";     // multicast announcement address     permission java.net.SocketPermission "224.0.1.84", "connect,accept";     // RMI and HTTP     permission java.net.SocketPermission "127.0.0.1:1024-", "connect,accept";     permission java.net.SocketPermission "*.canberra.edu.au:1024-",                                          "connect,accept";     permission java.net.SocketPermission "137.92.11.*:1024-",                                          "connect,accept,resolve";     permission java.net.SocketPermission "130.102.176.*:1024-",                                          "connect,accept,resolve";     permission java.net.SocketPermission "130.102.176.249:1024-",                                          "connect,accept,resolve";     // permission java.net.SocketPermission "137.92.11.117:1024-",                                             "connect,accept,resolve";     // debugging     permission java.lang.RuntimePermission "getProtectionDomain"; }; // Permissions for the application classes grant codeBase "file:/home/jan/projects/jini/doc/-" {     permission java.util.PropertyPermission "net.jini.discovery.*", "read";     permission net.jini.discovery.DiscoveryPermission "*";     // multicast request address     permission java.net.SocketPermission "224.0.1.85", "connect,accept";     // multicast announcement address     permission java.net.SocketPermission "224.0.1.84", "connect,accept";     // RMI and HTTP     permission java.net.SocketPermission "127.0.0.1:1024-", "connect,accept";     permission java.net.SocketPermission "*.canberra.edu.au:1024-",                                          "connect,accept";     permission java.net.SocketPermission "137.92.11.*:1024-",                                          "connect,accept,resolve";     permission java.net.SocketPermission "130.102.176.*:1024-",                                          "connect,accept,resolve";     permission java.net.SocketPermission "130.102.176.249:1024-",                                          "connect,accept,resolve";     // permission java.net.SocketPermission "137.92.11.117:1024-",                                             "connect,accept,resolve";       // debugging     permission java.lang.RuntimePermission "getProtectionDomain";     // Add in any file, etc, permissions needed by the application classes }; 
  


A Programmer[ap]s Guide to Jini Technology
A Programmer[ap]s Guide to Jini Technology
ISBN: 1893115801
EAN: N/A
Year: 2000
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net