Usage examples

<< Back to index

This page demonstrates the usage of MPL with various small example policies related to sockets, function call sequences, and the file system. The examples follow the modular structure of MPL policies and can be woven either independently or together using the policy inheritance mechanism.

Allowed hosts policy

In this example, one would like to allow network access only to a discrete, limited set of hosts and to a second set of hosts defined by a regular expression pattern. The set of hosts (allowedHosts) and the regex pattern (allowedPatterns) are defined statically (the policy file must be recompiled, if these need to be modified) in the policy file and the policy is enforced at runtime. The set contains these hosts: localhost and 127.0.0.1. The regex pattern matches all hosts beginning with the word internal and ending with company.

The application connects to an RMI server using standard Java API function Naming.lookup and opens socket connections by constructing Socket objects. Each time a connection attempt is made using these methods, the policy checks to see, whether the remote URL belongs to one of the sets of allowed hosts. Because the RMI URLs are prefixed with rmi://, the policy also needs to extract the common hostname part from the URL. If a policy violation is detected, by default the system will shutdown immediately by calling the shutdown method of MPLSimpleShutdown object.

Listing allowed_hosts_policy.mpl shows the policy definition, and file allowed_hosts_policy.aj is the corresponding generated AspectJ code. The sets of allowed hosts are expressed with set and pattern variables. The policy is enforced by using simple conditions attached to the respective RMI lookup and socket construction methods. Whether the given URL belongs to the sets is checked with the overloaded includes operator. The prefix can be filtered with the split method from the standard library.

The MPL compiler always compiles the policy into a single aspect. Policy variables become members of the aspect. The builtin set and pattern data types are converted to MPLSet and Pattern types, and literals are processed, when necessary. The mplExit member object is used to shut down the system when a policy violation occurs. The last part of the generated output consists of policy rules converted to pointcuts in a straightforward manner. Basically a new pointcut is created for each rule, unless the rules have identical signatures, in which case the compiler groups the rules under the same pointcut in the same order they appear in the policy definition file.

Socket connection policy

This example is built on top of the RMI & socket application from Example 1 and makes use of the same intercepted methods. With this policy one would like to limit socket connections to the host with whom the application has an active RMI connection. To simplify the task, it is assumed that RMI connection attempts always succeed.

The policy reduces to checking whether the remembered active RMI URL and URL used to construct Sockets are equivalent. Each time an RMI lookup operation is executed, the policy stores the address of the remote host to a policy variable lastRMIHost. Again the rmi:// prefix needs to be preprocessed. The equivalence test mentioned above will be performed just before a new socket object is created.

Listing socket_conn_policy.mpl shows the policy definition. The policy definition is very similar to the policy definition of the previous example. Most notable change is the use of the variable lastRMIHost to remember the host name value. The value of the variable is updated with the code inside the effect blocks, after the captured method has been executed successfully.

Message sending policy

This policy monitors the amount of messages sent via ObjectOutputStream objects connected to Sockets. In addition it counts the total number of characters sent within messages. A policy violation occurs whenever any of the predefined limits are exceeded. The messages are classified into three categories based on their length. Each category has its own counter, but the counter of total number of characters is shared.

In this example the three message counters have limits of 5, 6, and 7 messages (allowedMsgCount). Maximum number of allowed characters is limited to 250 (allowedMsgCharCount). The variables msgCounters and msgCharCounter are used as dynamic counters, respectively. Messages with the length of 0..9 characters (lowerCharLimit) belong to the first category, messages with 10..30 characters to the second and messages longer than 30 characters (upperCharLimit) to the third category. Message limits are always checked before a message is sent. The policy keeps track of various streams in order to make sure that the checking routine only monitors streams connected to sockets.

Listing message_policy.mpl shows the policy definition and listing message_policy.aj is the generated AspectJ code. As mentioned above, the variables allowedMsgCount, allowedMsgCharCount, lowerCharLimit, and upperCharLimit act as predefined limits for the respective message and character counters. Since the MPL does not support local variables in policy rules, a global len is used to simplify the pre-effect block. The last three variables are containers that are used to keep track of the chains of streams attached to sockets. The first three rules update these variables each time a new stream or socket object is constructed. The last rule first updates the message and character counters according to the contents of the message inside the pre-effect block, and after that verifies the states of the counters in the condition block.

Call sequence policy 1

This policy monitors the call sequence of Java methods by remembering whether a call to a certain method is still open. The idea is to allow calling a certain other method only inside an open call. In this case funB() is only allowed to be called from inside the funA().

Listing fun_sequence_policy1.mpl shows the policy definition. The variable openCallsOfA is to mark whether a call of funA() is still open. The value has to be updated in the pre-effects block of funA(), since ordinary effects are processed only after the monitored call and thus fail to capture chained calls. The role of corresponding effects block is to clear the marker. Since openCallsOfA is a counter, the possible recursion of funA() is handled properly.

Controlling the calling of funB() is straightforward.

It is worth noting that requiring that funB() is only called directly from funA() would have been much harder to implement with the current version of MPL, since preventing indirect calls of funB() via funA() would require us to catch all possible methods called directly from funA(). Unfortunately, MPL has no easy construction for that purpose.

Call sequence policy 2

This policy monitors the call sequence of Java methods by preventing subsequent calls to method funA() unless the method funB() has been called between the calls, and vice versa.

Listing fun_sequence_policy2.mpl shows the policy definition. The two variables currentFun and previousFun can be considered as a simple FIFO call queue. The queue has to be updated in the pre-effects block, since ordinary effects are processed only after the monitored call and thus fail to capture chained calls. The use of pre-effects also ensures that recursion is handled properly.

Each time a method is called, it erases the oldest name from the ``queue'' and remembers the name of the current method. The pre-effects are followed by the condition which checks the name of the previous call in funA() and funB().

File system policy

In this policy changing the user directory and listing of files in other directories are forbidden. In practice the desired functionality is achieved by placing guards before the listing methods and the property update method. Since the setProperty method is also used to update other properties, the guard must check the values only when it is called with the key ``user.dir''. The guards before listing methods simply compare the given path to the ``user.dir'' property stored by the Java platform.

Listing directory_policy.mpl shows the policy definition. The policy rules in this example are simple conditions. The remembered user directory value is stored by the standard library and only read by the policy code.

Inheriting policies

One can also combine related policies, such as the first three examples shown above, using the import feature of MPL. The system automatically quarantees that no conflicting variable signatures will be used in the resulting policy. However, the order or imports is important, since it has an effect on the order of effect blocks. The generated output is basically the same as the concatenated output of policies above, but the system organizes related functionality (i.e. policy rules with similar signatures) under the same pointcuts, as already mentioned.

Listing policygroup.mpl shows the policy definition.

Usage instructions

Running the MPL compiler:

java -classpath mplc.jar:/path/to/antlr-runtime-x.y.z.jar
    mpl.MPLc policyfile.mpl

mplc.jar contains all MPLc compiler's classes except ANTLR's runtime.

Compiling the aspects:

ajc -inpath /path/to/binaries
    -classpath /path/to/aspectj1.6/lib/aspectjrt.jar:mplrt.jar:/path/to/binaries
    -source 1.5
    /path/to/aspects/policyfile.java
    -outjar weaved_application.jar

The compilation also expects to find a file called META-INF/aop.xml from the search path. The following can be used as a simple starting point in these examples:

<aspectj>
        <aspects>
                <aspect name="policygroup"/>
        </aspects>

        <weaver options="-verbose -Xset:weaveJavaPackages=true -Xset:weaveJavaxPackages=true">
                <include within="javax.*"/>
                <include within="java.*"/>
                <include within="rmisockettest.*"/>
        </weaver>
</aspectj>
        
aop.xml

Running the application:

aj5 -classpath /path/to/aspectjrt.jar:weaved_application.jar:mplrt.jar
    -Djava.security.policy=my_application.policy
    myapp.Main

mplrt.jar contains the MPLc runtime classes (MPLSet etc.). A policy file with appropriate permissions set is needed to enable the load time weaving and socket connections. The following permissions are granted in these examples:

grant {
  permission java.net.SocketPermission
    "*:1024-65536", "connect, accept, listen, resolve";
  permission java.util.PropertyPermission "aj.weaving.verbose", "read";
  permission java.util.PropertyPermission "org.aspectj.tracing.messages", "read";
  permission java.util.PropertyPermission "org.aspectj.weaver.showWeaveInfo", "read";
  permission java.util.PropertyPermission "org.aspectj.weaver.loadtime.configuration", "read";
  permission java.lang.RuntimePermission "accessDeclaredMembers";
  permission java.lang.RuntimePermission "accessClassInPackage.{rmisockettest}";
  permission java.lang.RuntimePermission "createClassLoader";
  permission java.lang.RuntimePermission "getClassLoader";
};
        
my_application.policy