In addition to the methods of the security manager class that we just examined, a second way by which the security manager can enforce policies is to ask that the class loader for a particular class provide more information on which the security manager may base its decision. This technique requires a coordination between the security manager and the class loader; there is no standard interface by which this information may be obtained (nor is there a limit to the type of information that may be exchanged). The details of the interface are completely at the discretion of the application developer. This technique is useful for both 1.1-based and (to a lesser extent) 1.2-based security managers.
In the last section, we showed an example of the checkWrite() method that threw a security exception only if there was a class loader on the stack; this effectively prevented any class that was loaded from the network from opening a file in order to write to it. A more sophisticated policy would be to allow certain classes loaded over the network to write files, but not other classes. If you recall our example from Chapter 3, "Java Class Loaders", XYZ Corporation is using a customized class loader that allows their applications to read classes both from the web server on which the application is hosted and from the centralized administration server. XYZ Corporation might want to establish a security policy whereby classes that are loaded from the administration server can write local files, but other classes cannot. This sort of policy requires some cooperation between the security manager and the class loader--the security manager must ask the class loader for the host the class was loaded from:
public void checkWrite(String s) { ClassLoader cl = currentClassLoader(); if (cl != null) { MultiLoader ml = null; try { ml = (MultiLoader) cl; } catch (ClassCastException cce) { // This can't happen unless our class loader and our // security manager are out of sync throw new SecurityException("checkWrite out of sync"); } if (!ml.getTrust(currentLoadedClass())) throw new SecurityException("checkWrite"); } }
This example only works with a class loader we have defined, since we need a method called getTrust() in the class loader to let us know the origin of the class. That getTrust() method might look like this:
public class MultiLoader extends SecureClassLoader { ... boolean getTrust(Class c) { String name = c.getName(); if (supportClassesCache.get(name)) return true; return false; } }
Hence, we cast the class loader returned from getClassLoader() to be an instance of MultiLoader. It's easy to keep the class loader and the security manager in sync because the application must install both of them, but it always pays to be sure. We use this class loader to check whether the particular class is to be trusted; the class loader thinks classes that have been loaded from XYZ's support machine are trusted and other classes are not. Note that if you are going to use this technique in 1.2 that it is quite possible the class loader will not be your multi loader--it might be one of the internal class loaders that is used to load extension or API classes. In that case, instead of throwing a security exception when the class cast fails, you should simply call the super.checkWrite() method, which will do the correct thing in 1.2.
This sort of cooperation can be used between the class loader and the security manager to support a variety of requirements--providing different access to classes from different domains, or from different protocols, or anything else the class loader knows about. It just requires that the security manager know about any special interfaces the class loader might have to support these features.
The relationship between the security manager and class loader goes both ways--not only is the class loader able to provide additional information about particular classes to the security manager, the class loader is also responsible for calling the security manager to see if particular classes are able to be loaded or defined. We showed the code a class loader uses to do this in Chapter 3, "Java Class Loaders".
When a class loader is asked to load a class, it must call the checkPackageAccess() method of the security manager so that the security manager can prevent certain classes from being loaded. This is chiefly used to prevent untrusted classes from directly accessing implementation-specific classes. If you ship an application with a set of classes in the com.XYZ package, you can ensure that untrusted classes do not directly call classes in that package by placing the appropriate logic into the checkPackageAccess() method. Java-enabled browsers typically do just that; for example, an applet cannot call any of the classes in the sun package within the HotJava browser.
Additionally, when a class loader is asked to define a class, it must call the checkPackageDefinition() method of the security manager so that the security manager can prevent an untrusted class from defining classes in a particular package. This should be used, for example, to prevent an untrusted class from loading a new class into the java.lang package. Otherwise, an untrusted class could create a class named java.lang.Foo that has access to all the default-protected methods and instance variables of the other classes within the java.lang package.
Copyright © 2001 O'Reilly & Associates. All rights reserved.
This HTML Help has been published using the chm2web software. |