Rework the injected script:
authorMark Donnelly <mark@painless-security.com>
Thu, 16 Oct 2014 19:53:26 +0000 (15:53 -0400)
committerMark Donnelly <mark@painless-security.com>
Thu, 16 Oct 2014 19:53:26 +0000 (15:53 -0400)
* Make it object-based
* Functions now take hashes (like jQuery) instead of parameter lists
* There is now a single response dispatcher instead of writing one
  per GSS method call
* Implement the gss_error C-macro as a JavaScript function
* Split the callback into two callbacks: success and error.
* Add checking for whether the message returned from the C code has a
  matching nonce/method tuple.

chrome/app/navigator.gss.js
chrome/test/test.html

index b011894..7af3b44 100644 (file)
-console.log('Loading navigator.gss.js - #5');
+console.log('Loading navigator.gss.js - #7');
 
 /* This file gets injected into the web page verbatim */
 
-navigator.gss_callbacks = {};
 
-/*
-navigator.generateNonce = function() {
-  // TODO: Make sure that we don't have a collision!
-  // Random integer in the range [0..(2^32-1)]
-  return Math.floor(Math.random() * ( 4294967295 )) ;
-}
-*/
 
-navigator.gss_import_name = function(name, mech, callbackFn, appTag){
-    var nonce = navigator.generateNonce();
-    navigator.gss_callbacks[nonce] = callbackFn;
-    
-    /* Listen for a message back from the content script */
-    window.addEventListener(
-      "message",
-      function(event)
-      {
-        var app_tag;
-        var name;
-        var callback;
+var GSSEap = (function () 
+  {
+    function GSSEap(config)
+    {
+        // Public attributes
+        this.version = "0.0.1";
+        this.callbacks = {};
+        this.methods = {};
+        this.appTag = config.appTag || "";
+        this.error = config.error || function (major, minor, error, appTag) {
+            console.warn(error);
+        };
+        window.addEventListener("message", this.dispatch_responses.bind(this));
+    }
+    GSSEap.prototype.dispatch_responses = function (event) 
+    {
+        var method;
         var nonce;
-        
-        if (event.data.method != "gss_import_name" ||
-            (typeof(event.data.return_values) == "undefined") )
+        var callback;
+        var app_tag;
+
+        /* This message is destined for us only if all the following apply:
+        * - The data.method_name is one of the methods implemented by this
+        *   object
+        * - data.return_values exists
+        * - data.cookies exists
+        * - One of my callbacks matches the nonce in data.cookies.navigator_gss_tag
+        * - One of my methods matches the nonce in data.cookies.navigator_gss_tag
+        *   and that method matches data.method
+        */
+        method = event.data.method;
+        if (
+             (method != "gss_import_name") ||
+             ("undefined" == typeof (event.data.return_values)) ||
+             ("undefined" == typeof (event.data.cookies)))
         {
-          return;
+            return;
         }
-        
-        var nonce = event.data.cookies.navigator_gss_tag;
+
+        nonce = event.data.cookies.navigator_gss_tag;
         event.data.cookies.navigator_gss_tag = undefined;
-        callback = navigator.gss_callbacks[nonce];
-        navigator.gss_callbacks[nonce] = undefined;
-        
-        // Extract the data from the returned JSON
-        name = event.data.return_values.gss_name;
+        callback = this.callbacks[nonce];
+        if ("undefined" == typeof (callback) || this.methods[nonce] != method) {
+            return;
+        }
+
+        // We now know that this message is for us!
+        this.callbacks[nonce] = undefined;
         app_tag = event.data.cookies.app_tag;
-        major = event.data.return_values.major_status;
-        minor = event.data.return_values.minor_status;
-        
-        // Invoke the callback with the extracted data
-        callback(name, major, minor, app_tag);
-      }
-    );
 
-    /* Send a message off to the extension that we want to 
-     * call gss_import_name
-     */
-    window.postMessage({
-       "method":"gss_import_name",
-       "arguments":
-       {
-           "input_name": name,
-           "input_name_type": mech
-       },
-        "cookies":
+        if (this.gss_error(event.data.return_values.major_status))
+        {
+            var errMsg = "Error during " + method + ": " + 
+              "Major status message: " + 
+              event.data.return_values.errors.major_status_message + 
+              "; Minor status message: " + 
+              event.data.return_values.errors.minor_status_message;
+            this.error(
+              event.data.return_values.major_status,
+              event.data.return_values.minor_status, 
+              errMsg,
+              app_tag);
+        } else {
+            if ("gss_import_name" == method) 
+            { callback(event.data.return_values.gss_name, app_tag); }
+            // if( "gss_X" == method ) { callback(event.data.return_values.x, app_tag);}
+        }
+    };
+
+    GSSEap.prototype.import_name = function (params) 
+    {
+        /* variables */
+        var nonce;
+        var name = params.name;
+        var name_type = params.name_type || "{1 2 840 113554 1 2 1 4 }";
+        var callback = params.success;
+        var error = params.error || this.error; 
+        var app_tag = params.app_tag || this.appTag;
+
+        /* Erorr checking */
+        // Call an error if we don't have the required parameters.
+        // - name
+        // - success()
+        if ( "undefined" == typeof(name) ||
+             "undefined" == typeof(success) )
         {
-            "navigator_gss_tag": nonce,
-            "app_tag": appTag
+          this.error(-1, -1, 
+            "import_name called missing either name or success callback"
+          );
+          return;
         }
-    }, "*");
+        
+        nonce = navigator.generateNonce();
+        this.callbacks[nonce] = callback;
+        this.methods[nonce] = "gss_import_name";
+        window.postMessage({
+            "method":"gss_import_name",
+            "arguments":
+            {
+                "input_name": name,
+                "input_name_type": name_type
+            },
+            "cookies":
+            {
+                "navigator_gss_tag": nonce,
+                "app_tag": app_tag
+            }
+        }, "*");
+        
+    };
+
+    GSSEap.prototype.gss_error = function (major) 
+    {
+        var callingMask;
+        var routineMask;
+        var mask;
+
+        callingMask = 377 << 24;
+        routineMask = 377 << 16;
+        mask = callingMask | routineMask;
+
+        return (0 != (major & mask));
+    };
+    return GSSEap;
+})();
 
-};
+navigator.gss_eap = GSSEap;
index 650a065..f5cb715 100644 (file)
@@ -8,22 +8,30 @@
       }
 
       function doImportName() {
-        var ret = navigator.gss_import_name( 
-          document.getElementById('import_name_name').value,
-          document.getElementById('import_name_mech').value,
-          'nonce',
-          function(name, nonce, major, minor) {
-            report('GSS imported name: ' + name);
-            report('GSS imported nonce: ' + nonce);
-            report('GSS imported major status: ' + major);
-            report('GSS imported minor status: ' + minor);
-          }
-        );
+        var gss = new navigator.gss({
+          appTag: "TestApp",
+          error:  function(major, minor, errMsg, appTag) 
+                  {
+                    report("Error");
+                    report("Major: " + major + "; Minor: " + minor);
+                    report("<blockquote>" + errMsg + "</blockquote>");
+                    report("appTag: " + appTag);
+                  }
+        });
+        gss.import_name({
+          name:      document.getElementById('import_name_name').value,
+          name_type: document.getElementById('import_name_mech').value,
+          success:   function(name, appTag) {
+                       report("GSS imported name: " + name);
+                       report("appTag: " + appTag);
+                     }
+        });
       }
       
       document.addEventListener('DOMContentLoaded', function () {
         document.getElementById('import_name').addEventListener(
-          'click', doImportName);
+          'click', doImportName
+        );
         console.log('DOMContentLoaded.');
       });