Thursday, January 15, 2015

Loading DLL in a web-based applet: Part 2

I'd embarked on this mission a couple of months ago to get an applet working.

The downside to that earlier solution, was the requirement to amend the java.policy file on the client machine.

TL;DR -
  1. Run while-loop in applet;
  2. Poll webpage for action flag;
  3. Map public methods to equivalent JavaScript object;
  4. Break JavaScript function into pre-phase and post-phase;
  5. Instruct applet to trigger post-phase upon completion.

The permission that had to be added could well have been AllPermission if I hadn't took the time to drill down into getting the more specific Permissions needed:

grant {
    permission java.util.PropertyPermission "mapAnyUriToUri", "read";
    permission java.lang.RuntimePermission "accessDeclaredMembers";
    permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
};

How I dug these gems out was by a somewhat tedious combination of
  1. Telling the Java console to load verbose (Java Control Panel > Advanced tab > Debugging > Enable tracing), where all the permissions being checked for are output into the console, and
  2. Generally setting the JRE to debug mode (-Djava.compiler=NONE -Xnoagent -Xdebug -Xrunjdwp:transport=dt_socket,address=2502,server=y,suspend=y) in conjunction with connecting via my Eclipse IDE with the advice from here.

That said, I eventually realised after testing, that the above triplet of Permissions was set for the sake of letting JavaScript call the applet without being thrown PrivilegeActionExceptions. My misconception was that the permissions were to allow my applet to call the DLL when in fact, it was already happily doing its job within the chunk of doPrivileged code I followed from here.

As it turned out, this method did not sit well with my peers, so I had to refactor it one way or another. I scoured the web for solutions and this is what I managed to gather from hints all over:
  • This tightened security only began around Java 7u51;
  • Signed applets can call unsecured JavaScript functions happily;
  • JavaScript functions are impossible to be secured/signed thus universally untrusted;
  • This means security limitations would disallow JavaScript to trigger applet methods directly (unless the Permissions above were granted globally);
  • A forum post hinted at running a while-loop thread in the applet that polls the webpage.
And here's the eventual change of plans I had in place:
  • Test starting a Thread according to this;
  • Realised the DLL would end up in a separate ClassLoader as a result;
  • Maintained the run() within the applet itself (aka without starting it in a separate Thread, and this is achievable only because the applet merely runs in the background and doesn't have any UI component);
  • This run() acts as a listener of sorts, polling the webpage for requests to perform tasks;
  • The direct references to the applet on the webpages were transitioned to map to a JavaScript object;
  • This object (contained in its own .js file) is basically a JavaScript variation of the method calls I'd implemented in the applet;
  • Each function of this object passes in a callback method that the applet will trigger upon completion of its part.
This is how the JavaScript object looks like roughly:
//thisApplet.js
var thisApplet = {
    //fields
    flag: "",
    args: [],
    callback: "",
    propertyA: "",
    propertyB: "",
  
    //applet handles
    getFlag: function() {
        return this.flag;
    },
  
    getArgs: function() {
        return this.args;
    },
  
    doneSignal: function(retVar, propertyA, propertyB) {
        this.flag = "";
        this.args = [];
        this.propertyA = propertyA;
        this.propertyB = propertyB;
        if(this.callback != "") {
            this.callback(retVar);
        }
    },
  
    //properties called via JavaScript
    getPropertyA: function() {
        return this.propertyA;
    },
  
    getPropertyB: function() {
        return this.propertyB;
    },
  
    //applet-equivalent functions called via JavaScript
    login: function(userId) {
        this.flag = "login";
        this.args = [userId];
        this.callback = function(retVar) {
            loginCallback(retVar);
        };
    },
  
    logout: function() {
        this.flag = "logout";
        this.args = [];
        this.callback = "";
    }
  
};
 The JS source has been sanitised, naturally.

Within the applet, I have a method for parsing the args array properly:
private String[] parseJSObjectArray(JSObject jso) {
    String[] args = null;
    Object len = jso.getMember("length");
    if(len instanceof Number) {
        int n = ((Number)len).intValue();
        args = new String[n];
        for(int i=0;i<args.length;i++) {
            args[i] = jso.getSlot(i).toString();
        }
       
    }
   
    return args;
}

Doing so allows me to avoid changing the rest of the JavaScript calls that I had originally referencing to the applet as much. And now with the added benefit of not requiring to amend the java.policy file, too.

No comments:

Post a Comment