Bug 41352: Always show the link to the manual in about:tor
[torbutton.git] / components / domain-isolator.js
blob1c77b5776f0fd1f7077bd4af88444dd3a8bf0151
1 // # domain-isolator.js
2 // A component for TorBrowser that puts requests from different
3 // first party domains on separate tor circuits.
5 // This file is written in call stack order (later functions
6 // call earlier functions). The code file can be processed
7 // with docco.js to provide clear documentation.
9 // ### Abbreviations
11 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
12 const { XPCOMUtils } = ChromeUtils.import(
13 "resource://gre/modules/XPCOMUtils.jsm"
16 XPCOMUtils.defineLazyModuleGetters(this, {
17 ComponentUtils: "resource://gre/modules/ComponentUtils.jsm",
18 });
20 // Make the logger available.
21 let logger = Cc["@torproject.org/torbutton-logger;1"].getService(Ci.nsISupports)
22 .wrappedJSObject;
24 // Import crypto object (FF 37+).
25 Cu.importGlobalProperties(["crypto"]);
27 // ## mozilla namespace.
28 // Useful functionality for interacting with Mozilla services.
29 let mozilla = {};
31 // __mozilla.protocolProxyService__.
32 // Mozilla's protocol proxy service, useful for managing proxy connections made
33 // by the browser.
34 mozilla.protocolProxyService = Cc[
35 "@mozilla.org/network/protocol-proxy-service;1"
36 ].getService(Ci.nsIProtocolProxyService);
38 // __mozilla.registerProxyChannelFilter(filterFunction, positionIndex)__.
39 // Registers a proxy channel filter with the Mozilla Protocol Proxy Service,
40 // which will help to decide the proxy to be used for a given channel.
41 // The filterFunction should expect two arguments, (aChannel, aProxy),
42 // where aProxy is the proxy or list of proxies that would be used by default
43 // for the given channel, and should return a new Proxy or list of Proxies.
44 mozilla.registerProxyChannelFilter = function(filterFunction, positionIndex) {
45 let proxyFilter = {
46 applyFilter(aChannel, aProxy, aCallback) {
47 aCallback.onProxyFilterResult(filterFunction(aChannel, aProxy));
50 mozilla.protocolProxyService.registerChannelFilter(
51 proxyFilter,
52 positionIndex
56 // ## tor functionality.
57 let tor = {};
59 // __tor.noncesForDomains__.
60 // A mutable map that records what nonce we are using for each domain.
61 tor.noncesForDomains = {};
63 // __tor.isolationEabled__.
64 // A bool that controls if we use SOCKS auth for isolation or not.
65 tor.isolationEnabled = true;
67 // __tor.unknownDirtySince__.
68 // Specifies when the current catch-all circuit was first used
69 tor.unknownDirtySince = Date.now();
71 // __tor.socksProxyCredentials(originalProxy, domain)__.
72 // Takes a proxyInfo object (originalProxy) and returns a new proxyInfo
73 // object with the same properties, except the username is set to the
74 // the domain, and the password is a nonce.
75 tor.socksProxyCredentials = function(originalProxy, domain) {
76 // Check if we already have a nonce. If not, create
77 // one for this domain.
78 if (!tor.noncesForDomains.hasOwnProperty(domain)) {
79 tor.noncesForDomains[domain] = tor.nonce();
81 let proxy = originalProxy.QueryInterface(Ci.nsIProxyInfo);
82 return mozilla.protocolProxyService.newProxyInfoWithAuth(
83 "socks",
84 proxy.host,
85 proxy.port,
86 domain, // username
87 tor.noncesForDomains[domain], // password
88 "", // aProxyAuthorizationHeader
89 "", // aConnectionIsolationKey
90 proxy.flags,
91 proxy.failoverTimeout,
92 proxy.failoverProxy
96 tor.nonce = function() {
97 // Generate a new 128 bit random tag. Strictly speaking both using a
98 // cryptographic entropy source and using 128 bits of entropy for the
99 // tag are likely overkill, as correct behavior only depends on how
100 // unlikely it is for there to be a collision.
101 let tag = new Uint8Array(16);
102 crypto.getRandomValues(tag);
104 // Convert the tag to a hex string.
105 let tagStr = "";
106 for (let i = 0; i < tag.length; i++) {
107 tagStr += (tag[i] >>> 4).toString(16);
108 tagStr += (tag[i] & 0x0f).toString(16);
111 return tagStr;
114 tor.newCircuitForDomain = function(domain) {
115 // Re-generate the nonce for the domain.
116 if (domain === "") {
117 domain = "--unknown--";
119 tor.noncesForDomains[domain] = tor.nonce();
120 logger.eclog(
122 "New domain isolation for " + domain + ": " + tor.noncesForDomains[domain]
126 // __tor.clearIsolation()_.
127 // Clear the isolation state cache, forcing new circuits to be used for all
128 // subsequent requests.
129 tor.clearIsolation = function() {
130 // Per-domain nonces are stored in a map, so simply re-initialize the map.
131 tor.noncesForDomains = {};
133 // Force a rotation on the next catch-all circuit use by setting the creation
134 // time to the epoch.
135 tor.unknownDirtySince = 0;
138 // __tor.isolateCircuitsByDomain()__.
139 // For every HTTPChannel, replaces the default SOCKS proxy with one that authenticates
140 // to the SOCKS server (the tor client process) with a username (the first party domain)
141 // and a nonce password. Tor provides a separate circuit for each username+password
142 // combination.
143 tor.isolateCircuitsByDomain = function() {
144 mozilla.registerProxyChannelFilter(function(aChannel, aProxy) {
145 if (!tor.isolationEnabled) {
146 return aProxy;
148 try {
149 let channel = aChannel.QueryInterface(Ci.nsIChannel),
150 firstPartyDomain = channel.loadInfo.originAttributes.firstPartyDomain;
151 if (firstPartyDomain === "") {
152 firstPartyDomain = "--unknown--";
153 if (Date.now() - tor.unknownDirtySince > 1000 * 10 * 60) {
154 logger.eclog(
156 "tor catchall circuit has been dirty for over 10 minutes. Rotating."
158 tor.newCircuitForDomain("--unknown--");
159 tor.unknownDirtySince = Date.now();
162 let replacementProxy = tor.socksProxyCredentials(
163 aProxy,
164 firstPartyDomain
166 logger.eclog(
168 `tor SOCKS: ${channel.URI.spec} via
169 ${replacementProxy.username}:${replacementProxy.password}`
171 return replacementProxy;
172 } catch (e) {
173 logger.eclog(4, `tor domain isolator error: ${e.message}`);
174 return null;
176 }, 0);
179 // ## XPCOM component construction.
180 // Module specific constants
181 const kMODULE_NAME = "TorBrowser Domain Isolator";
182 const kMODULE_CONTRACTID = "@torproject.org/domain-isolator;1";
183 const kMODULE_CID = Components.ID("e33fd6d4-270f-475f-a96f-ff3140279f68");
185 // DomainIsolator object.
186 function DomainIsolator() {
187 this.wrappedJSObject = this;
190 // Firefox component requirements
191 DomainIsolator.prototype = {
192 QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
193 classDescription: kMODULE_NAME,
194 classID: kMODULE_CID,
195 contractID: kMODULE_CONTRACTID,
196 observe(subject, topic, data) {
197 if (topic === "profile-after-change") {
198 logger.eclog(3, "domain isolator: set up isolating circuits by domain");
200 if (Services.prefs.getBoolPref("extensions.torbutton.use_nontor_proxy")) {
201 tor.isolationEnabled = false;
203 tor.isolateCircuitsByDomain();
206 newCircuitForDomain(domain) {
207 tor.newCircuitForDomain(domain);
210 enableIsolation() {
211 tor.isolationEnabled = true;
214 disableIsolation() {
215 tor.isolationEnabled = false;
218 clearIsolation() {
219 tor.clearIsolation();
222 wrappedJSObject: null,
225 // Assign factory to global object.
226 const NSGetFactory = XPCOMUtils.generateNSGetFactory
227 ? XPCOMUtils.generateNSGetFactory([DomainIsolator])
228 : ComponentUtils.generateNSGetFactory([DomainIsolator]);