CS 5331SC – INDIVIDUAL PROJECT FINAL REPORT

JAVA`S SECURITY ARCHITECTURE

– CUSTOMIZING POLICY IMPLEMENTATION –

DANIELA INCLEZAN

 

 

 

POLICY FILES CAPABILITIES

                  Policy files were introduced in the context of applets. (An applet is a program that can be included in an HTML page, much in the same way an image is included. When you use a Java technology-enabled browser to view a page that contains an applet, the applet`s code is transferred to your system and executed by the browser`s Java Virtual Machine (JVM).) In the beginning, for security reasons, the applets` abilities to affect the machine they were executed on or to obtain sensitive information about the machine were severely restricted. Then the concept of "signed applet" appeared and with it the idea that some permissions should be granted to code coming from a trusted source.

 

                  Policy file syntax:

grant signedBy "signer_names", codeBase "URL",

               principal principal_class_name "principal_name",

              principal principal_class_name "principal_name",

              ... {

 

      permission permission_class_name "target_name", "action",

          signedBy "signer_names";

      permission permission_class_name "target_name", "action",

          signedBy "signer_names";

      ...

        };

        

         signedBy (in grant) – who signed the code specified in CodeBase

         principal – who executes the code

         signedBy (in permission) – who signed the permission_class

        

 

         Policy file example:

 

         grant codebase "http://www.games.com", signedBy "Duke",

                       principal javax.security.auth.x500.X500Principal "cn=Alice" {

               permission java.io.FilePermission "/tmp/input.txt", "read, write";

        };

                 

Grant the permission to read and write the file "/tmp/input.txt" to code coming from "http://www.games.com", signed by Duke and executed by Alice.

 

PROBLEM

Java`s security framework provides a default implementation of the Policy abstract class: PolicyFile. This implementation has some week points:

- the policy permissions are stored in files in plaintext

- it doesn`t support role based access control and

- no refreshes are performed after the policy file is first read when the JVM is started.

In addition, if policy files need to be enforced on a large number of hosts, managing all these files (and synchronizing the modifications done to them) can be very difficult.

 

POSSIBLE SOLUTION

The solution I considered appropriate to solve all these problems is to store the permissions in one central database instead of having one policy file per host. This way, managing the permissions is very easy, since any modifications are performed in one place only and not at every host. In order to handle the retrieval of permission information from the database, a new implementation of the Policy abstract class needs to be provided. The communication between the customized implementation of Policy and the database must be secured so that the security problem with the policy files is not repeated. The refresh problem can be solved by providing a refresh() method that is called on a regular basis and retrieves the latest information from the database. The database implementation has the advantage that it permits a flexible design that could also enforce role base access control.

 

MY GOAL INSIDE THE PROBLEM

With this database solution in mind, I tried to create a simple version of the database Policy customization. My intention was to see what are the steps that must be performed in order to create a customized implementation of the Policy class and what problems might appear in the process. I created a DatabasePolicy class that reads permissions from a previously filled embedded database. My implementation doesn`t try to cover role based access control, neither does it try to solve the refresh problem. As a database I used the McKoi embedded database.

 

DATABASEPOLICY IMPLEMENTATION

                  In order to develop this basic customization of the Policy abstract class, I considered it would be very useful for me to rely on already implemented and tested Java code (the source files being available). I found that the default implementation PolicyFile was very much what I needed for my implementation of DatabasePolicy. The behavior of PolicyFile was exactly what I needed, except for the fact that PolicyFile read the information from a file. Thus I had to replace the method that was processing the policy file with another method that was reading information from the database.

                  PolicyFile relies on a PolicyParser class to parse the policy file(s). PolicyParser produces a vector called grantEntries of GrantEntry objects. PolicyParser uses a series of nested classes: PermissionEntry, PrincipalEntry, GrantEntry (containing a LinkedList of PrincipalEntry objects and a Vector of PermissionEntry objects). These classes reflect the structure of the policy file:

grant signedBy "signer_names", codeBase "URL",

        principal principal_class_name "principal_name",

        principal principal_class_name "principal_name",

        ... {

 

      permission permission_class_name "target_name", "action",

          signedBy "signer_names";

      permission permission_class_name "target_name", "action",

          signedBy "signer_names";

      ...

  };

                  PolicyFile uses the vector of GrantEntry objects provided by the PolicyParser in order to create a list of policy entries (a PolicyEntry object is a (CodeSource, Permission) pair) that is stored in a PolicyInfo inner class.

DatabasePolicy follows this model (Appendix A – Class Diagram). I replaced the PolicyParser with a DatabasePolicyEntry class that produces a vector of GrantEntries. DatabasePolicyEntry is helped by the MckoiPolicyDao class to retrieve the information from the database. MckoiPolicyDao takes care of all communication with the database. It also inserts the default permissions in the database if it sees the database is empty (the first time DatabasePolicy is used). I also replaced the method that was calling the PolicyParser (init()) with a method that uses DatabasePolicyEntry: initDatabasePolicy-FromDatabase().

The database schema (Appendix B) was designed so that it would reflect the structure of the policy file.

 

Note:

One important thing to be mentioned here is that PolicyFile looks into at least two files for permission information because there is by default a system-wide policy file and a user policy file. The system policy file contains basic permissions for the JVM to be able to run (allow a thread to stop itself, allow anyone to listen on un-privileged ports and read standard properties). If the system file doesn`t exist, PolicyFile has a static method that adds the same permissions to the collection of permissions. The basic permissions are hard coded in that method.

For the DatabasePolicy implementation I chose to rely on the static method copied from PolicyFile in order to make sure that the basic permissions are added. I stored in the database the information that would otherwise go in the user policy file (the "real" policy file).

 

Pseudo-code

 

Class DatabasePolicy extends java.security.Policy{

 

private List policies; // list of PolicyEntries

private List grantEntries;

//list of GrantEntries fetched from the database

DatabasePolicy()  // constructor

{

If database doesn`t exist or is empty

Create database and/ or fill it with reading permission for file "input.txt";

List grantEntries = Get_GrantEntries_from_Database();

init();

}

 

init() {

                  initStaticPolicy();

                  initDatabasePolicyFromDatabase();

}

 

intiStaticPolicy() {

Add to policies the permissions to allow a thread to stop itself;

Add to policies the permissions to allow everyone to listen to un-privileged ports;

Add to policies the permissions to allow everyone to read standard java properties; 

}

 

initDatabasePolicyFromDatabase() {

         For (i=0; i<grantEntries.size(); i++) {

                  Create a new PolicyEntry;

Fill PolicyEntry with the information in grantEntries.get(i);

                  Add PolicyEntry to policies;

}

}

 

// the rest of the code is more or less the same as in PolicyFile

 

PermissionCollection getPermissions(CodeSource cs) {

         // uses policies List

}

 

PermissionCollection getPermissions(ProtectionDomain pd) {

         // uses policies List

}

 

boolean implies(ProtectionDomain pd, Permission p) {}

void refresh()

 

. . .

}

 

GIVING CONTROL TO MY DATABASEPOLICY

                  In order to test my customization class, I had to change the default policy provider. Since I performed the testing on a machine that was used by other students too, I installed a new JVM in my home directory and I made the setting changes to that JVM. When running the test program I used the JVM installed in my home directory for that.

                  In the java.security file found at: my_jvm\jre\lib\security, there is a property called policy.provider. By default its value is sun.security.provider.PolicyFile. I changed it to the complete name of my DatabasePolicy class.

                  I also had to make sure DatabasePolicy was available to the bootstrap ClassLoader for loading. There were two ways to make that happen: either adding DatabasePolicy class to rt.jar in my_jvm\jre\lib or creating a "classes" directory under my_jvm\jre and placing DatabasePolicy there. I chose the first solution. Besides the actual DatabasePolicy class I had to add all related classes (MckoiPolicyDao, GrantEntry, etc.) and the packages required by the McKoi embedded database (mckoidb.jar and mkjdbc.jar).

 

TESTING GOALS

 

TESTING

                  After all these steps have been done, I ran the test program using my modified JVM. The test program relied on the fact that the reading permission for file "input.txt" was added in the database, but the writing permission to this file was not. The test program first displays the current policy object (in order to check it is DatabasePolicy and not the default PolicyFile), then tries to read from "input.txt" and finally it tries to write to "input.txt".

                  The output of running the test program (Appendix C - b) indicated that indeed the policy object was an instance of DatabasePolicy, that the reading operation succeeded as expected, and the writing operation did not, again as expected. An AccessControl-Exception was thrown indicating that writing to the "input.txt" file was not allowed.

 

                  Note:

                  In order to test a Policy customized implementation, the SecurityManager that performs all the checks for allowed or not allowed actions must be enabled. This is done by running the test program with the option: "-Djava.security.manager".

                 

 

CONCLUSIONS

                  I conclude that providing a customized implementation of the Policy abstract class can be very helpful when the application requires more security or more flexibility than the default implementation can offer. I consider that the default implementation is very helpful in designing and implementing the new customized implementation because the code doesn`t need to be written from scratch. I used the existing code and made the required changes in order to make my customization work as planned.

                  In my simple customization, the permissions introduced in the database are hard-coded. In the future, an interface with the user can be created to allow the administrator to manage the permissions in the database.

 

 

CLASS DIAGRAM

 

DATABASE SCHEMA

 

TEST PROGRAM OUTPUT

 

 

a) Running the test program without enabling the SystemManager => all permissions are granted because permission checks are not performed:

 

D:\dincleza>%JAVA_HOME%\bin\java PolicyTester

Current policy is an instance of: sorcer.core.provider.policer.DatabasePolicy

TRYING TO READ FROM ALLOWED FILE

    It should succeed

    Current policy is instance of: sorcer.core.provider.policer.DatabasePolicy

 

TRYING TO WRITE TO UNALLOWED FILE

    It should fail and throw an AccessControlException

    Current policy is instance of: sorcer.core.provider.policer.DatabasePolicy

 

 

 

 

b) Running the test program with the SystemManager enabled => the permissions that are not in the database (writing to file "input.txt") are not granted.

 

D:\dincleza>%JAVA_HOME%\bin\java -Djava.security.manager PolicyTester

Current policy is an instance of: sorcer.core.provider.policer.DatabasePolicy

TRYING TO READ FROM ALLOWED FILE

    It should succeed

    Current policy is instance of: sorcer.core.provider.policer.DatabasePolicy

 

TRYING TO WRITE TO UNALLOWED FILE

    It should fail and throw an AccessControlException

    Current policy is instance of: sorcer.core.provider.policer.DatabasePolicy

Exception in thread "main" java.security.AccessControlException: access denied (java.io.FilePermission D:\dincleza\input.txt write)

        at java.security.AccessControlContext.checkPermission(AccessControlConte

xt.java:264)

        at java.security.AccessController.checkPermission(AccessController.java:

427)

        at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)

        at java.lang.SecurityManager.checkWrite(SecurityManager.java:962)

        at java.io.FileOutputStream.<init>(FileOutputStream.java:169)

        at java.io.FileOutputStream.<init>(FileOutputStream.java:70)

        at PolicyTester.main(PolicyTester.java:22)