summaryrefslogtreecommitdiff
blob: e95b223d96e33a12915162731f6262e146ef4320 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
From 0607517f87c2a0060f4f1e437f0ac37a889047a9 Mon Sep 17 00:00:00 2001
From: Mike Perry <mikeperry-git@torproject.org>
Date: Wed, 2 May 2012 17:44:39 -0700
Subject: [PATCH 14/20] Prevent WebSocket DNS leak.

This is due to an improper implementation of the WebSocket spec by Mozilla.

"There MUST be no more than one connection in a CONNECTING state.  If multiple
connections to the same IP address are attempted simultaneously, the client
MUST serialize them so that there is no more than one connection at a time
running through the following steps.

If the client cannot determine the IP address of the remote host (for
example, because all communication is being done through a proxy server that
performs DNS queries itself), then the client MUST assume for the purposes of
this step that each host name refers to a distinct remote host,"

https://tools.ietf.org/html/rfc6455#page-15

They implmented the first paragraph, but not the second...

While we're at it, we also prevent the DNS service from being used to look up
anything other than IP addresses if socks_remote_dns is set to true, so this
bug can't turn up in other components or due to 3rd party addons.
---
 netwerk/dns/nsDNSService2.cpp                   |   24 ++++++++++++++++++++++-
 netwerk/dns/nsDNSService2.h                     |    1 +
 netwerk/protocol/websocket/WebSocketChannel.cpp |    8 +++++-
 3 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp
index 7458729..40fbaba 100644
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -403,6 +403,7 @@ nsDNSService::Init()
     bool     enableIDN        = true;
     bool     disableIPv6      = false;
     bool     disablePrefetch  = false;
+    bool     disableDNS       = false;
     int      proxyType        = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
     
     nsAdoptingCString ipv4OnlyDomains;
@@ -426,6 +427,10 @@ nsDNSService::Init()
 
         // If a manual proxy is in use, disable prefetch implicitly
         prefs->GetIntPref("network.proxy.type", &proxyType);
+
+        // If the user wants remote DNS, we should fail any lookups that still
+        // make it here.
+        prefs->GetBoolPref("network.proxy.socks_remote_dns", &disableDNS);
     }
 
     if (mFirstTime) {
@@ -443,7 +448,7 @@ nsDNSService::Init()
 
             // Monitor these to see if there is a change in proxy configuration
             // If a manual proxy is in use, disable prefetch implicitly
-            prefs->AddObserver("network.proxy.type", this, false);
+            prefs->AddObserver("network.proxy.", this, false);
         }
     }
 
@@ -472,6 +477,7 @@ nsDNSService::Init()
         mIDN = idn;
         mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
         mDisableIPv6 = disableIPv6;
+        mDisableDNS = disableDNS;
 
         // Disable prefetching either by explicit preference or if a manual proxy is configured 
         mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
@@ -583,6 +589,14 @@ nsDNSService::AsyncResolve(const nsACString  &hostname,
         if (mDisablePrefetch && (flags & RESOLVE_SPECULATE))
             return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
 
+        PRNetAddr tempAddr;
+        if (mDisableDNS) {
+            // Allow IP lookups through, but nothing else.
+            if (PR_StringToNetAddr(hostname.BeginReading(), &tempAddr) != PR_SUCCESS) {
+                return NS_ERROR_UNKNOWN_PROXY_HOST; // XXX: NS_ERROR_NOT_IMPLEMENTED?
+            }
+        }
+
         res = mResolver;
         idn = mIDN;
     }
@@ -669,6 +683,14 @@ nsDNSService::Resolve(const nsACString &hostname,
         MutexAutoLock lock(mLock);
         res = mResolver;
         idn = mIDN;
+
+        PRNetAddr tempAddr;
+        if (mDisableDNS) {
+            // Allow IP lookups through, but nothing else.
+            if (PR_StringToNetAddr(hostname.BeginReading(), &tempAddr) != PR_SUCCESS) {
+                return NS_ERROR_UNKNOWN_PROXY_HOST; // XXX: NS_ERROR_NOT_IMPLEMENTED?
+            }
+        }
     }
     NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
 
diff --git a/netwerk/dns/nsDNSService2.h b/netwerk/dns/nsDNSService2.h
index 1749b41..3ec8eba 100644
--- a/netwerk/dns/nsDNSService2.h
+++ b/netwerk/dns/nsDNSService2.h
@@ -70,4 +70,5 @@ private:
     bool                      mDisableIPv6;
     bool                      mDisablePrefetch;
     bool                      mFirstTime;
+    bool                      mDisableDNS;
 };
diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp
index 3578866..b1aaec2 100644
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -1884,8 +1884,12 @@ WebSocketChannel::ApplyForAdmission()
   LOG(("WebSocketChannel::ApplyForAdmission: checking for concurrent open\n"));
   nsCOMPtr<nsIThread> mainThread;
   NS_GetMainThread(getter_AddRefs(mainThread));
-  dns->AsyncResolve(hostName, 0, this, mainThread, getter_AddRefs(mDNSRequest));
-  NS_ENSURE_SUCCESS(rv, rv);
+  rv = dns->AsyncResolve(hostName, 0, this, mainThread, getter_AddRefs(mDNSRequest));
+  if (NS_FAILED(rv)) {
+      // Fall back to hostname on dispatch failure
+      mDNSRequest = nsnull;
+      OnLookupComplete(nsnull, nsnull, rv);
+  }
 
   return NS_OK;
 }
-- 
1.7.5.4