commit 59445b7baed58e712bd38c02e6dc75882ff0c997 Author: Arthur Edelstein arthuredelstein@gmail.com Date: Mon Jul 14 00:40:13 2014 -0700
Bug #3455.3: Add DomainIsolator, for isolating circuit by domain.
An XPCOM component that registers a ProtocolProxyChannelFilter which sets the username/password for each web request according to url bar domain. --- src/chrome.manifest | 5 +- src/components/domain-isolator.js | 128 +++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-)
diff --git a/src/chrome.manifest b/src/chrome.manifest index af44862..d211984 100644 --- a/src/chrome.manifest +++ b/src/chrome.manifest @@ -155,7 +155,10 @@ contract @torproject.org/torbutton-torCheckService;1 {5d57312b-5d8c-4169-b4af-e8 component {f36d72c9-9718-4134-b550-e109638331d7} components/torbutton-logger.js contract @torproject.org/torbutton-logger;1 {f36d72c9-9718-4134-b550-e109638331d7}
+component {e33fd6d4-270f-475f-a96f-ff3140279f68} components/domain-isolator.js +contract @torproject.org/domain-isolator;1 {e33fd6d4-270f-475f-a96f-ff3140279f68} + category profile-after-change CookieJarSelector @torproject.org/cookie-jar-selector;1 -# category profile-after-change RefSpoofer @torproject.org/torRefSpoofer;1 category profile-after-change TBSessionBlocker @torproject.org/torbutton-ss-blocker;1 category profile-after-change StartupObserver @torproject.org/startup-observer;1 +category profile-after-change DomainIsolator @torproject.org/domain-isolator;1 \ No newline at end of file diff --git a/src/components/domain-isolator.js b/src/components/domain-isolator.js new file mode 100644 index 0000000..e8e6bfa --- /dev/null +++ b/src/components/domain-isolator.js @@ -0,0 +1,128 @@ +// # domain-isolator.js +// A component for TorBrowser that puts requests from different +// first party domains on separate tor circuits. + +// This file is written in call stack order (later functions +// call earlier functions). The code file can be processed +// with docco.js to provide clear documentation. + +/* jshint moz: true */ +/* global Components, console, XPCOMUtils */ + +// ### Abbreviations +const Cc = Components.classes, Ci = Components.interfaces, Cu = Components.utils; + +// Make the logger available. +let logger = Cc["@torproject.org/torbutton-logger;1"] + .getService(Components.interfaces.nsISupports).wrappedJSObject; + +// ## mozilla namespace. +// Useful functionality for interacting with Mozilla services. +let mozilla = mozilla || {}; + +// __mozilla.protocolProxyService__. +// Mozilla's protocol proxy service, useful for managing proxy connections made +// by the browser. +mozilla.protocolProxyService = Cc["@mozilla.org/network/protocol-proxy-service;1"] + .getService(Ci.nsIProtocolProxyService); + +// __mozilla.thirdPartyUtil__. +// Mozilla's Thirdy Party Utilities, for figuring out first party domain. +mozilla.thirdPartyUtil = Cc["@mozilla.org/thirdpartyutil;1"] + .getService(Ci.mozIThirdPartyUtil); + +// __mozilla.registerProxyChannelFilter(filterFunction, positionIndex)__. +// Registers a proxy channel filter with the Mozilla Protocol Proxy Service, +// which will help to decide the proxy to be used for a given channel. +// The filterFunction should expect two arguments, (aChannel, aProxy), +// where aProxy is the proxy or list of proxies that would be used by default +// for the given channel, and should return a new Proxy or list of Proxies. +mozilla.registerProxyChannelFilter = function (filterFunction, positionIndex) { + let proxyFilter = { + applyFilter : function (aProxyService, aChannel, aProxy) { + return filterFunction(aChannel, aProxy); + } + }; + mozilla.protocolProxyService.registerChannelFilter(proxyFilter, positionIndex); +}; + +// ## tor functionality. +let tor = tor || {}; + +// __tor.noncesForDomains__. +// A mutable map that records what nonce we are using for each domain. +tor.noncesForDomains = {}; + +// __tor.socksProxyCredentials(originalProxy, domain)__. +// Takes a proxyInfo object (originalProxy) and returns a new proxyInfo +// object with the same properties, except the username is set to the +// the domain, and the password is a nonce. +tor.socksProxyCredentials = function (originalProxy, domain) { + // Check if we already have a nonce. If not, create + // one for this domain. + if (!tor.noncesForDomains.hasOwnProperty(domain)) { + tor.noncesForDomains[domain] = 0; + } + let proxy = originalProxy.QueryInterface(Ci.nsIProxyInfo); + return mozilla.protocolProxyService + .newSOCKSProxyInfo(proxy.host, + proxy.port, + domain, // username + tor.noncesForDomains[domain].toString(), // password + proxy.flags, + proxy.failoverTimeout, + proxy.failoverProxy); +}; + +// __tor.isolateCircuitsByDomain()__. +// For every HTTPChannel, replaces the default SOCKS proxy with one that authenticates +// to the SOCKS server (the tor client process) with a username (the first party domain) +// and a nonce password. Tor provides a separate circuit for each username+password +// combination. +tor.isolateCircuitsByDomain = function () { + mozilla.registerProxyChannelFilter(function (aChannel, aProxy) { + try { + let channel = aChannel.QueryInterface(Ci.nsIHttpChannel), + firstPartyURI = mozilla.thirdPartyUtil.getFirstPartyURIFromChannel(channel, true) + .QueryInterface(Ci.nsIURI), + firstPartyDomain = mozilla.thirdPartyUtil + .getFirstPartyHostForIsolation(firstPartyURI), + proxy = aProxy.QueryInterface(Ci.nsIProxyInfo), + replacementProxy = tor.socksProxyCredentials(aProxy, firstPartyDomain); + logger.eclog(3, "tor SOCKS: " + channel.URI.spec + " via " + + replacementProxy.username + ":" + replacementProxy.password); + return replacementProxy; + } catch (err) { + // If we fail, then just use the default proxyInfo. + return aProxy; + } + }, 0); +}; + +// ## XPCOM component construction. +// Module specific constants +const kMODULE_NAME = "TorBrowser Domain Isolator"; +const kMODULE_CONTRACTID = "@torproject.org/domain-isolator;1"; +const kMODULE_CID = Components.ID("e33fd6d4-270f-475f-a96f-ff3140279f68"); + +// Import XPCOMUtils object. +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +// DomainIsolator object. Constructor does nothing. +function DomainIsolator() { } +// Firefox component requirements +DomainIsolator.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), + classDescription: kMODULE_NAME, + classID: kMODULE_CID, + contractID: kMODULE_CONTRACTID, + observe: function (subject, topic, data) { + if (topic === "profile-after-change") { + logger.eclog(3, "domain isolator: set up isolating circuits by domain"); + tor.isolateCircuitsByDomain(); + } + } +}; + +// Assign factory to global object. +const NSGetFactory = XPCOMUtils.generateNSGetFactory([DomainIsolator]);
tor-commits@lists.torproject.org