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 idunknown
push userunknown
push dateunknown
reviewerspeterv, jst, beta8
bugs616733
milestone2.0b8pre
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);