Java in Notes/Domino Explained: On Java Security and how it relates to Notes/Domino


I get so many questions on Java security and how it all fits together that I finally decided to blog it instead of simply answering over and over again. So here goes…

Security was built into Java from its inception and everything in a Java Virtual Machine (JVM) runs under the control of a security manager. Now the security manager may be very lax or it may be very restrictive as the one for Java applets or Notes/Domino agents is. A security manager is installed into the JVM when it’s constructed meaning either from the command line or, in the case of Notes/Domino, when a JVM is created to run agents, XPages or the like. A security manager may decide that a certain operation (method call) is “powerful” and hence will require the code executing the operation to be allowed to do so.

The way this is done in Java is by the operation itself (the method you are calling e.g. “write file” or “do stuff with reflection”) asking the security manager whether the code calling it has a given permission that it supplies to the security manager. The security manager may either decide that it does and hence return quietly or it may decide that the calling code does not have the required permission. To signal the latter it will throw a java.security.AccessControlException which you’ve seen if you’re reading this.

So how do we grant these permissions?

In Java permissions are controlled in the java.policy file located in the file system under <notes>/jvm/lib/security or <domino>/jvm/lib/security. I will not go into detail on the syntax of this file as resources are readily available on the web but suffice to say that different locations (be those file based or network based) are granted different sets of permissions (for more info see http://docs.oracle.com/javase/6/docs/technotes/guides/security/PolicyFiles.html). By default jar-files in jvm/lib/ext, the XPages runtime, OSGi bundles etc. are granted all permissions possible (java.security.AllPermission) whereas other code is granted a limited set of permissions. So if you need to run code that requires a different set of permissions you edit this file and restart the server/client. Now often times simply editing this file is not an option. Either you do not control the server, you cannot edit it for all clients or your security guidelines tell you just can’t do it. In either case editing the file is seldom the entire solution. And you need to understand why. Before showing you some code let me explain the scenario I’m going to use below first.

The scenario is that Joe Notes Developer needs to use reflection (wikipedia) to change the accessibility of a method from private to public (yes it’s possible) to override the private restriction set on the generate() method of the Test class (see below). To do this he uses a method like the one below:

public void doAccessible(Object obj) throws Exception {
   Method[] methods = obj.getClass().getMethods();
   for (int i=0; i<methods.length; i++) {
      Method m = methods[i];
      m.setAccessible(true);
   }
}

The class he needs to affect is likewise simple (he needs to make the generate() method public):

private class Test {
   private String uuid = null;

   public Test() {
      this.generate();
   }

   public String getUUID() {
      return this.uuid;
   }

   private void generate() {
      this.uuid = UUID.randomUUID().toString();
   }
}

To start of Joe Notes Developer creates a Java agent in Domino Designer and sticks the below code in NotesMain of the agent:

Test t = new Test();
System.out.println(t.getUUID());
doAccessible (t);
t.generate();
System.out.println(t.getUUID());
System.out.println("Completed...");

But oh no!! Joe gets the dreaded AccessControlExeption as the code in the Java agent may not use reflection this way.

java.security.AccessControlException: Access denied (java.lang.reflect.ReflectPermission suppressAccessChecks)
   at java.security.AccessController.checkPermission(AccessController.java:132)
   at java.lang.SecurityManager.checkPermission(SecurityManager.java:544)
   at COM.ibm.JEmpower.applet.AppletSecurity.superDotCheckPermission(AppletSecurity.java:1449)
   at COM.ibm.JEmpower.applet.AppletSecurity.checkPermission(AppletSecurity.java:1617)
   at COM.ibm.JEmpower.applet.AppletSecurity.checkPermission(AppletSecurity.java:1464)
   at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:118)
   at ReflectionUtil.doAccessible(Unknown Source)
   at JavaAgent.NotesMain(Unknown Source)
   at lotus.domino.AgentBase.runNotes(Unknown Source)
   at lotus.domino.NotesThread.run(Unknown Source)

Nothing is holding Joe back though so he puts the reflection code into a ReflectionUtil class which he puts in a jar-file in jvm/lib/ext as he has heard that this location allows for this kind of funky stuff. Using the ReflectionUtil instead of a class method from his agent is like this:

ReflectionUtil l = new ReflectionUtil();
Test t = new Test();
System.out.println(t.getUUID());
l.doAccessible(t);
t.generate();
System.out.println(t.getUUID());
System.out.println("Completed...");

Arrrghhhh no!! He still gets an AccessControlException. But how can this be?? He did put the code in the special jvm/lib/ext folder which is granted AllPermissions. Hmmm…

The reason is that security in Java is layered and it’s the layer or “context” in Java parlance initiating the call that decides which permissions are assigned. This is why simply making a call from a Java agent in Notes/Domino to a Java library in jvm/lib/ext that need permissions other than the default set doesn’t just work. It doesn’t work because it’s the context of the Java agent which is in effect. So wouldn’t it be great if you could change the context? Well you can… 🙂 This is done using the java.security.AccessController class (docs).
The AccessController class is a nifty little thing. It works by you supplying an instance of the java.security.PrivilegedAction interface which is an interface with a single method called run. Inside the run-method you put the code requiring special permissions. Below is an example of using the AccessController class:

AccessController.doPrivileged(new PrivilegedExceptionAction() {
   public Object run() throws Exception {
      ReflectionUtil l = new ReflectionUtil();
      Test t = new Test();
      l.doAccessible(t);
      System.out.println("Completed...");
      return null;
   }
});

But wouldn’t you know it – this also fails! Of course the reason is quite obvious – I think – we cnanot just have developers go ahead elevating their persmissions themselves. As Paul Mooney would say – developers mus be controlled and kept down. It needs to be the privileged code meaning the code in jvm/lib/etc that needs to elevate the permissions. To do this we move the AccessController call info the ReflectionUtil in a special “doAccessiblePrivileged” method as this code can then decide to elevate permissions as it runs from a location with a higher security context. The code in the agent is therefor now like this (change in bold):

ReflectionUtil l = new ReflectionUtil();
Test t = new Test();
System.out.println(t.getUUID());
l.doAccessiblePrivileged(t);
t.generate();
System.out.println(t.getUUID());
System.out.println("Completed...");

And the code in ReflectionUtil looks like this:

public void doAccessiblePrivileged(final Object obj) throws Exception {
   AccessController.doPrivileged(new PrivilegedExceptionAction() {
      @Override
      public Object run() throws Exception {
         ReflectionUtil.this.doAccessible(obj);
         return null;
      }
   });
}

Now the code runs just fine as Joe is using code that knows that it might need to elevate the permissions so it provides a call doing just that (doAccessiblePrivileged).

Of course this can make it very difficult to just use a standard library from the web in Notes as it may not provide a way to elevate the permissions as no one ever saw that as a use case. In this case a wrapper API that elevates the permissions in jvm/lib/ext may be necessary to make it work.

Hope this explains it… 🙂

The example code is available for download including the required jar-file to put in jvm/lib/ext.

11 thoughts on “Java in Notes/Domino Explained: On Java Security and how it relates to Notes/Domino”

  1. And the real fun begins when you are working with callbacks and want to make this elevation code secure. It’s easy to call a simple JVM method like setAccessible(boolean) or create a FileOutputStream, but imaging that a callback should get called within the AccessController block. Then that callback gets called with full privileges and can do whatever it wants in the system.

    So you should think before you work with AccessController.doPrivileged(…).

    BTW: Do you know a way to reduce permissions again within the AccessController block?

    Like

  2. Hi Mikkel,

    Thanks for this post! Very useful!

    I am currently facing a case were I also get an AccessControlException, but in a slightly different context. In my Web application (Domino-based), I use an applet which is loaded with JWS. The JNLP file and all the classes I use are stored as JAR files in a "resources applet" design element of my database. The JARs are all signed with the same certificate and the manifest file of all JARs contains the "Trusted-Library: true" parameter. Everything was fine with my applet, until the day I tried to add a third-party JAR containing a new Swing L&F to be used in my applet. As soon as I have added this JAR (also signed with my certificate), I have been unable to run my applet with Java 1.6 (works fine with 1.7). The plug-in first warns about "mixed signed and unsigned code", then the following error occurs :

    <p style="margin-left: 40px;">java.security.AccessControlException: access denied (java.lang.RuntimePermission accessClassInPackage.sun.swing.plaf.synth)

    There is perhaps a reflection call somewhere in the code of the third-party L&F, but as I don’t have the sources, it is difficult to say. Anyway, I have been in contact with the support team of the company selling this L&F and after some tests, we have spotted that my applet was running file as soon as the JNLP and JARs files were moved outside the database. Even adding the files as simple "resource files" design element was not enough to prevent the errors at runtime. So, I have placed all my files in the "dominohtml" folder of my Domino server, and everything was fine without any error regarding the security.

    Now, to return to the original subject of your post (sorry for this long explanation), my question is, do you have an idea of what is controlling the privileges of Java resources when those resources are design elements of a database?

    If you are curious to test it yourself, I can even provide you a small sample. Just tell me.

    Like

  3.  Phillippe —

    I’m happy to hear that the post was of use to you. As to your particular question I’ve never seen this myself. When ever a resource is hosted inside a database it is governed by the ACL of the database and the directory the database is in. But if the ACL allows for access you should be okay. Only thing I can think of is if the resource is loaded through some other means where the credentials are not set correctly and hence the resource isn’t loaded correctly. Since there is no ACL what so ever when loading from domino/html might be why it works from there. Only way to diagnose this is probably by having a proxy monitoring the traffic on your client. I use Charles Web Proxy for stuff like that.

    Good luck

    Like

  4. I have added your JavaLib to my Java agent and put the Test method and reflectionutil code in my NotesMain, but still getting these error messages when using the com.ibm.db2.jcc.DB2Driver class.  When I add the db2jcc.jar to the build path I get a ‘ClassNotFoundException’ and if I add it as an archive to my agent some other error.

    ef655702-c6ca-40d7-b58d-e73fbe32baf4

    Runtime Exception…

    java.security.AccessControlException: Access denied (java.lang.reflect.ReflectPermission suppressAccessChecks)

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

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

    at COM.ibm.JEmpower.applet.AppletSecurity.superDotCheckPermission(AppletSecurity.java:1449)

    at COM.ibm.JEmpower.applet.AppletSecurity.checkPermission(AppletSecurity.java:1617)

    at COM.ibm.JEmpower.applet.AppletSecurity.checkPermission(AppletSecurity.java:1464)

    at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:118)

    at ReflectionUtil.doAccessible(Unknown Source)

    at ReflectionUtil$1.run(Unknown Source)

    at java.security.AccessController.doPrivileged(AccessController.java:277)

    at ReflectionUtil.doAccessiblePrivileged(Unknown Source)

    at JavaAgent.NotesMain(JavaAgent.java:69)

    at lotus.domino.AgentBase.runNotes(Unknown Source)

    at lotus.domino.NotesThread.run(Unknown Source)

    java.lang.ClassNotFoundException: com.ibm.db2.jcc.DB2Driver

    at java.lang.Class.forNameImpl(Native Method)

    at java.lang.Class.forName(Class.java:141)

    at JavaAgent.NotesMain(JavaAgent.java:97)

    at lotus.domino.AgentBase.runNotes(Unknown Source)

    <span class="Apple-tab-span" style="white-space: pre;"> </span>at lotus.domino.NotesThread.run(Unknown Source) 

    Like

  5. Hi Mikkel,

    I am getting the below exception when am trying to access a server from Lotus Notes java agent.

    <p style="margin-left: 40px;"> java.security.AccessControlException: Access denied (javax.net.ssl.SSLPermission setHostnameVerifier)

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

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

    What am exactly doing is,creating some JSON header and posting it to another(not domino) server.For each and every method am calling a method to verfiy cert and it should return host name valid true.

    This is working fine if am running it as a standalone java program but not in lotus notes.

    Here what I did is,I just created a class with the below certificate verification code

    <p style="margin-left: 40px;">private void trustCerts () {

            JOptionPane.showMessageDialog(null,"Am in trustCerts——–");

            try{

                // Create a trust manager that does not validate certificate chains

                TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {

                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {

                            return null;

                        }

                        public void checkClientTrusted(X509Certificate[] certs, String authType) {

                        }

                        public void checkServerTrusted(X509Certificate[] certs, String authType) {

                        }

                    }

                };

                // Install the all-trusting trust manager

                SSLContext sc = SSLContext.getInstance("SSL");

                sc.init(null, trustAllCerts, new java.security.SecureRandom());

                HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

                JOptionPane.showMessageDialog(null,"before return from trustcert——–");

                // Create all-trusting host name verifier

                HostnameVerifier allHostsValid = new HostnameVerifier() {

                    public boolean verify(String hostname, SSLSession session) {

                        return true;

                    }

                };

                // Install the all-trusting host verifier

                HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

               

            }catch(Exception e){

                e.printStackTrace();

            }

        }

    and exported this as a jar file and placed it in this IBMLotusNotesjvmlibext directory.

    In my class I just created object for the class which is inside of my jar and called the method.

    I dont know whether what I did is correct or not..Am pretty new to Lotus Notes….Please adivce me to get out of this.

     

    Thanks

    Chitra

    Like

  6. Chitra,

    I am having the exact same error you had regarding the sslhostnameverifier.  Is there any way to provide more details on how you created the class and jar as I am unsure how to do that.

     

    Thanks,

     

    STeveR

    Like

  7.  Steven, I have the same question.  Did you ever get an answer from Chitra or did you get it to work yourself?

    – Fred

    Like

  8.   The issue is with the Security Manager installed in IBM Notes and IBM Domino. That security manager restricts certain operations in the Java language such as wriiting to files on disk etc. The error you are seeing is that Domino restricts you from "messing" with system classes. 
    The way to solve such issues with AccessControlExceptions is to 1) find another way to do what you are trying to do which is the easiest or 2) move the code to a location with more rights than where your code is executing now (i.e. an agent) and call that code with elevated rights and have that code work its magic for you. I’ve written about how this elevation of rights work here >> https://lekkimworld.com/2013/06/20/java_in_notes_domino_explained_on_java_security_and_how_it_relates_to_notes_domino.html
    Hope that helps

    Like

Comments are closed.