Bug 1223481 - Use the "potentially trustworthy origin" helper to validate Push server URLs. r=dragana
authorKit Cambridge <kcambridge@mozilla.com>
Tue, 10 Nov 2015 10:50:46 -0800
changeset 306352 f793597159bc98cd5507f6d1ae728c6c9fdad5e1
parent 306351 16fc1974dab6bf94f80f545750d184c660a6f702
child 306353 66a799321ef851242374893b00c88df336bd8137
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1223481
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1223481 - Use the "potentially trustworthy origin" helper to validate Push server URLs. r=dragana
dom/push/PushService.jsm
dom/push/PushServiceHttp2.jsm
dom/push/PushServiceWebSocket.jsm
dom/push/test/xpcshell/test_register_5xxCode_http2.js
dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js
dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js
dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js
dom/push/test/xpcshell/test_updateRecordNoEncryptionKeys_http2.js
dom/security/nsContentSecurityManager.cpp
--- a/dom/push/PushService.jsm
+++ b/dom/push/PushService.jsm
@@ -22,16 +22,20 @@ const {PushServiceHttp2} = Cu.import("re
 const {PushCrypto} = Cu.import("resource://gre/modules/PushCrypto.jsm");
 
 // Currently supported protocols: WebSocket.
 const CONNECTION_PROTOCOLS = [PushServiceWebSocket, PushServiceHttp2];
 
 XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",
                                   "resource://gre/modules/AlarmService.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gContentSecurityManager",
+                                   "@mozilla.org/contentsecuritymanager;1",
+                                   "nsIContentSecurityManager");
+
 this.EXPORTED_SYMBOLS = ["PushService"];
 
 XPCOMUtils.defineLazyGetter(this, "console", () => {
   let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
   return new ConsoleAPI({
     maxLogLevelPref: "dom.push.loglevel",
     prefix: "PushService",
   });
@@ -322,26 +326,44 @@ this.PushService = {
     try {
       Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
       return "network-active-changed";
     } catch (e) {
       return "network:offline-status-changed";
     }
   },
 
-  _findService: function(serverURI) {
-    var uri;
-    var service;
-    if (serverURI) {
-      for (let connProtocol of CONNECTION_PROTOCOLS) {
-        uri = connProtocol.checkServerURI(serverURI);
-        if (uri) {
-          service = connProtocol;
-          break;
-        }
+  _findService: function(serverURL) {
+    console.debug("findService()");
+
+    let uri;
+    let service;
+
+    if (!serverURL) {
+      console.warn("findService: No dom.push.serverURL found");
+      return [];
+    }
+
+    try {
+      uri = Services.io.newURI(serverURL, null, null);
+    } catch (e) {
+      console.warn("findService: Error creating valid URI from",
+        "dom.push.serverURL", serverURL);
+      return [];
+    }
+
+    if (!gContentSecurityManager.isURIPotentiallyTrustworthy(uri)) {
+      console.warn("findService: Untrusted server URI", uri.spec);
+      return [];
+    }
+
+    for (let connProtocol of CONNECTION_PROTOCOLS) {
+      if (connProtocol.validServerURI(uri)) {
+        service = connProtocol;
+        break;
       }
     }
     return [service, uri];
   },
 
   _changeServerURL: function(serverURI, event) {
     console.debug("changeServerURL()");
 
@@ -423,32 +445,17 @@ this.PushService = {
 
     this._setState(PUSH_SERVICE_ACTIVATING);
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
 
     if (options.serverURI) {
       // this is use for xpcshell test.
 
-      var uri;
-      var service;
-      if (!options.service) {
-        for (let connProtocol of CONNECTION_PROTOCOLS) {
-          uri = connProtocol.checkServerURI(options.serverURI);
-          if (uri) {
-            service = connProtocol;
-            break;
-          }
-        }
-      } else {
-        try {
-          uri  = Services.io.newURI(options.serverURI, null, null);
-          service = options.service;
-        } catch(e) {}
-      }
+      let [service, uri] = this._findService(options.serverURI);
       if (!service) {
         this._setState(PUSH_SERVICE_INIT);
         return;
       }
 
       // Start service.
       this._startService(service, uri, false, options).then(_ => {
         // Before completing the activation check prefs. This will first check
--- a/dom/push/PushServiceHttp2.jsm
+++ b/dom/push/PushServiceHttp2.jsm
@@ -436,36 +436,18 @@ this.PushServiceHttp2 = {
   serviceType: function() {
     return "http2";
   },
 
   hasmainPushService: function() {
     return this._mainPushService !== null;
   },
 
-  checkServerURI: function(serverURL) {
-    if (!serverURL) {
-      console.warn("checkServerURI: No dom.push.serverURL found");
-      return;
-    }
-
-    let uri;
-    try {
-      uri = Services.io.newURI(serverURL, null, null);
-    } catch(e) {
-      console.warn("checkServerURI: Error creating valid URI from",
-        "dom.push.serverURL", serverURL);
-      return null;
-    }
-
-    if (uri.scheme !== "https") {
-      console.warn("checkServerURI: Unsupported scheme", uri.scheme);
-      return null;
-    }
-    return uri;
+  validServerURI: function(serverURI) {
+    return serverURI.scheme == "http" || serverURI.scheme == "https";
   },
 
   connect: function(subscriptions) {
     this.startConnections(subscriptions);
   },
 
   isConnected: function() {
     return this._mainPushService != null;
--- a/dom/push/PushServiceWebSocket.jsm
+++ b/dom/push/PushServiceWebSocket.jsm
@@ -198,36 +198,18 @@ this.PushServiceWebSocket = {
         if (requestTimedOut) {
           this._reconnect();
         }
       }
       break;
     }
   },
 
-  checkServerURI: function(serverURL) {
-    if (!serverURL) {
-      console.warn("checkServerURI: No dom.push.serverURL found");
-      return;
-    }
-
-    let uri;
-    try {
-      uri = Services.io.newURI(serverURL, null, null);
-    } catch(e) {
-      console.warn("checkServerURI: Error creating valid URI from",
-        "dom.push.serverURL", serverURL);
-      return null;
-    }
-
-    if (uri.scheme !== "wss") {
-      console.warn("checkServerURI: Unsupported websocket scheme", uri.scheme);
-      return null;
-    }
-    return uri;
+  validServerURI: function(serverURI) {
+    return serverURI.scheme == "ws" || serverURI.scheme == "wss";
   },
 
   get _UAID() {
     return prefs.get("userAgentID");
   },
 
   set _UAID(newID) {
     if (typeof(newID) !== "string") {
--- a/dom/push/test/xpcshell/test_register_5xxCode_http2.js
+++ b/dom/push/test/xpcshell/test_register_5xxCode_http2.js
@@ -74,17 +74,16 @@ add_task(function* test1() {
   do_test_pending();
   do_test_pending();
   do_test_pending();
 
   var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
 
   PushService.init({
     serverURI: serverURL + "/subscribe5xxCode",
-    service: PushServiceHttp2,
     db
   });
 
   let newRecord = yield PushNotificationService.register(
     'https://example.com/retry5xxCode',
     ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   );
 
--- a/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js
+++ b/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js
@@ -78,13 +78,12 @@ add_task(function* test1() {
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   PushService.init({
     serverURI: serverURL + "/subscribe",
-    service: PushServiceHttp2,
     db
   });
 
 });
--- a/dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js
+++ b/dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js
@@ -88,13 +88,12 @@ add_task(function* test1() {
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   PushService.init({
     serverURI: serverURL + "/subscribe",
-    service: PushServiceHttp2,
     db
   });
 
 });
--- a/dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js
+++ b/dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js
@@ -83,13 +83,12 @@ add_task(function* test1() {
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   PushService.init({
     serverURI: serverURL + "/subscribe",
-    service: PushServiceHttp2,
     db
   });
 
 });
--- a/dom/push/test/xpcshell/test_updateRecordNoEncryptionKeys_http2.js
+++ b/dom/push/test/xpcshell/test_updateRecordNoEncryptionKeys_http2.js
@@ -61,17 +61,16 @@ add_task(function* test1() {
 
   yield db.put(record);
 
   let notifyPromise = promiseObserverNotification('push-subscription-change',
                                                   _ => true);
 
   PushService.init({
     serverURI: serverURL + "/subscribe",
-    service: PushServiceHttp2,
     db
   });
 
   yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
     'Timed out waiting for notifications');
 
   let aRecord = yield db.getByKeyID(serverURL + '/subscriptionNoKey');
   ok(aRecord, 'The record should still be there');
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -420,17 +420,18 @@ nsContentSecurityManager::IsURIPotential
 
   *aIsTrustWorthy = false;
   nsAutoCString scheme;
   nsresult rv = aURI->GetScheme(scheme);
   NS_ENSURE_SUCCESS(rv, NS_OK);
 
   if (scheme.EqualsLiteral("https") ||
       scheme.EqualsLiteral("file") ||
-      scheme.EqualsLiteral("app")) {
+      scheme.EqualsLiteral("app") ||
+      scheme.EqualsLiteral("wss")) {
     *aIsTrustWorthy = true;
     return NS_OK;
   }
 
   nsAutoCString host;
   rv = aURI->GetHost(host);
   NS_ENSURE_SUCCESS(rv, NS_OK);