Bug 616733: Disable websockets by default. r=peterv,jst a=beta8
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 08 Dec 2010 14:12:51 -0800
changeset 58905 98d58c46e409404452f8327b0ac22d1cda5b1e17
parent 58904 19b83d59edbfcb6a5e40b91ce081731e7454804c
child 58906 ae0ba1e0f094609bbd7c2d69421700dab0c99d62
child 58909 c6377a0402153f34043a21791f10598089fdceb5
push id17458
push userjduell@mozilla.com
push dateWed, 08 Dec 2010 22:14:54 +0000
treeherdermozilla-central@98d58c46e409 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv, jst, beta8
bugs616733
milestone2.0b8pre
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 616733: Disable websockets by default. r=peterv,jst a=beta8 Can be turned on by setting preference network.websocket.override-security-block. Websockets can only be used if override-security-block and network.websocket.enabled are both set to true. At a future time, with a more secure websocket protocol, the override-security-block preference can be removed. This action is based on the security concern over an HTTP cache poisoning attack as described in http://www.adambarth.com/experimental/websocket.pdf
content/base/src/nsWebSocket.cpp
content/base/src/nsWebSocket.h
content/base/test/test_websocket.html
content/base/test/test_websocket_hello.html
dom/base/nsDOMClassInfo.cpp
modules/libpref/src/init/all.js
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -2919,19 +2919,17 @@ NS_IMETHODIMP
 nsWebSocket::Initialize(nsISupports* aOwner,
                         JSContext* aContext,
                         JSObject* aObject,
                         PRUint32 aArgc,
                         jsval* aArgv)
 {
   nsAutoString urlParam, protocolParam;
 
-  PRBool prefEnabled =
-    nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE);
-  if (!prefEnabled) {
+  if (!PrefEnabled()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   if (aArgc != 1 && aArgc != 2) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   JSAutoRequest ar(aContext);
@@ -3099,16 +3097,24 @@ nsWebSocket::CreateAndDispatchCloseEvent
 
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
   rv = privateEvent->SetTrusted(PR_TRUE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
 }
 
+PRBool
+nsWebSocket::PrefEnabled()
+{
+  return nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE) &&
+    nsContentUtils::GetBoolPref("network.websocket.override-security-block",
+                                PR_FALSE);
+}
+
 void
 nsWebSocket::SetReadyState(PRUint16 aNewReadyState)
 {
   nsresult rv;
 
   if (mReadyState == aNewReadyState) {
     return;
   }
@@ -3497,19 +3503,17 @@ nsWebSocket::Init(nsIPrincipal* aPrincip
                   nsPIDOMWindow* aOwnerWindow,
                   const nsAString& aURL,
                   const nsAString& aProtocol)
 {
   nsresult rv;
 
   NS_ENSURE_ARG(aPrincipal);
 
-  PRBool prefEnabled =
-    nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE);
-  if (!prefEnabled) {
+  if (!PrefEnabled()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   mPrincipal = aPrincipal;
   mScriptContext = aScriptContext;
   if (aOwnerWindow) {
     mOwner = aOwnerWindow->IsOuterWindow() ?
       aOwnerWindow->GetCurrentInnerWindow() : aOwnerWindow;
--- a/content/base/src/nsWebSocket.h
+++ b/content/base/src/nsWebSocket.h
@@ -98,16 +98,19 @@ public:
   NS_IMETHOD AddEventListener(const nsAString& aType,
                               nsIDOMEventListener *aListener,
                               PRBool aUseCapture,
                               PRBool aWantsUntrusted,
                               PRUint8 optional_argc);
 
   static void ReleaseGlobals();
 
+  // Determine if preferences allow WebSocket
+  static PRBool PrefEnabled();
+
 protected:
   nsresult ParseURL(const nsString& aURL);
   nsresult SetProtocol(const nsString& aProtocol);
   nsresult EstablishConnection();
 
   nsresult CreateAndDispatchSimpleEvent(const nsString& aName);
   nsresult CreateAndDispatchMessageEvent(nsCString *aData);
   nsresult CreateAndDispatchCloseEvent(PRBool aWasClean);
--- a/content/base/test/test_websocket.html
+++ b/content/base/test/test_websocket.html
@@ -588,29 +588,41 @@ function test22()
   ws.onopen = shouldNotOpen;
   ws.onclose = function(e)
   {
     shouldCloseNotCleanly(e);
     doTest(23);
   };
 }
 
+var domBranch;
+var oldPrefVal;
+
 function finishWSTest()
 {
   for (i = 0; i < all_ws.length; ++i) {
     if (all_ws[i] != shouldNotReceiveCloseEvent &&
         !all_ws[i]._receivedCloseEvent) {
       ok(false, "didn't called close on test " + all_ws[i]._testNumber + "!");
     }
   }
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  domBranch.setBoolPref("override-security-block", oldPrefVal);
   SimpleTest.finish();
 }
 
 function testWebSocket ()
 {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  var prefService =
+      Components.classes["@mozilla.org/preferences-service;1"]
+      .getService(Components.interfaces.nsIPrefService);
+  domBranch = prefService.getBranch("network.websocket.");
+  oldPrefVal = domBranch.getBoolPref("override-security-block");
+  domBranch.setBoolPref("override-security-block", true);
   doTest(first_test);
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 
--- a/content/base/test/test_websocket_hello.html
+++ b/content/base/test/test_websocket_hello.html
@@ -12,32 +12,48 @@
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=472529">Mozilla Bug </a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var ws;
+var oldPrefVal;
+var domBranch;
+
+function finishWSTest() {
+    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+    domBranch.setBoolPref("override-security-block", oldPrefVal);
+    SimpleTest.finish();
+}
 
 function testWebSocket () {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  var prefService =
+      Components.classes["@mozilla.org/preferences-service;1"]
+      .getService(Components.interfaces.nsIPrefService);
+  domBranch = prefService.getBranch("network.websocket.");
+  oldPrefVal = domBranch.getBoolPref("override-security-block");
+  domBranch.setBoolPref("override-security-block", true);
+
   ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket_hello");
   ws.onopen = function(e) {
     ws.send("data");
   }
   ws.onclose = function(e) {
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
-    SimpleTest.finish();
+    finishWSTest();
   }
   ws.onmessage = function(e) {
     is(e.data, "Hello world!", "Wrong data");
     ws.close();
-    SimpleTest.finish();
+    finishWSTest();
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 <div>
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -6340,16 +6340,23 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
   if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor ||
       name_struct->mType == nsGlobalNameStruct::eTypeExternalClassInfo) {
     // Don't expose chrome only constructors to content windows.
     if (name_struct->mChromeOnly &&
         !nsContentUtils::IsSystemPrincipal(aWin->GetPrincipal())) {
       return NS_OK;
     }
 
+    // For now don't expose web sockets unless user has explicitly enabled them
+    if (name_struct->mDOMClassInfoID == eDOMClassInfo_WebSocket_id) {
+      if (!nsWebSocket::PrefEnabled()) {
+        return NS_OK;
+      }
+    }
+
     // Create the XPConnect prototype for our classinfo, PostCreateProto will
     // set up the prototype chain.
     nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
     rv = GetXPCProto(sXPConnect, cx, aWin, name_struct,
                      getter_AddRefs(proto_holder));
 
     if (NS_SUCCEEDED(rv) && obj != aWin->GetGlobalJSObject()) {
       JSObject* dot_prototype;
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -747,16 +747,21 @@ pref("network.http.connection-retry-time
 // Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)
 // per Section 4.7 "Low-Latency Data Service Class".
 pref("network.ftp.data.qos", 0);
 pref("network.ftp.control.qos", 0);
 
 // </http>
 
 // <ws>: WebSocket
+// The -76 websocket network protocol may be subject to HTTP cache poisoning
+// attacks. Until there is a secure open standard available and implemented
+// in necko the override-security-block preference must be set to true before
+// the normal enabled preference is considered. Bug 616733
+pref("network.websocket.override-security-block", false);
 pref("network.websocket.enabled", true);
 // </ws>
 
 // If false, remote JAR files that are served with a content type other than
 // application/java-archive or application/x-jar will not be opened
 // by the jar channel.
 pref("network.jar.open-unsafe-types", false);