Verified Commit 74e39196 authored by Pier Angelo Vendrame's avatar Pier Angelo Vendrame 🎃
Browse files

fixup! Bug 10760: Integrate TorButton to TorBrowser core

Bug 40938: Moving the domain isolator out of torbutton
parent 98e75e48
Loading
Loading
Loading
Loading
+7 −92
Original line number Diff line number Diff line
@@ -46,32 +46,22 @@ var torbutton_new_circuit;
  // in a component, not the XUL overlay.
  var torbutton_unique_pref_observer = {
    register() {
      this.forced_ua = false;
      m_tb_prefs.addObserver("extensions.torbutton", this);
      m_tb_prefs.addObserver("browser.privatebrowsing.autostart", this);
      m_tb_prefs.addObserver("javascript", this);
      Services.prefs.addObserver("browser.privatebrowsing.autostart", this);
    },

    unregister() {
      m_tb_prefs.removeObserver("extensions.torbutton", this);
      m_tb_prefs.removeObserver("browser.privatebrowsing.autostart", this);
      m_tb_prefs.removeObserver("javascript", this);
      Services.prefs.removeObserver("browser.privatebrowsing.autostart", this);
    },

    // topic:   what event occurred
    // subject: what nsIPrefBranch we're observing
    // data:    which pref has been changed (relative to subject)
    observe(subject, topic, data) {
      if (topic !== "nsPref:changed") {
        return;
      }
      switch (data) {
        case "browser.privatebrowsing.autostart":
      if (
        topic === "nsPref:changed" &&
        data === "browser.privatebrowsing.autostart"
      ) {
        torbutton_update_disk_prefs();
          break;
        case "extensions.torbutton.use_nontor_proxy":
          torbutton_use_nontor_proxy();
          break;
      }
    },
  };
@@ -113,62 +103,6 @@ var torbutton_new_circuit;
    },
  };

  var torbutton_new_identity_observers = {
    register() {
      Services.obs.addObserver(this, "new-identity-requested");
    },

    observe(aSubject, aTopic, aData) {
      if (aTopic !== "new-identity-requested") {
        return;
      }

      // Clear the domain isolation state.
      torbutton_log(3, "Clearing domain isolator");
      const domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService(
        Ci.nsISupports
      ).wrappedJSObject;
      domainIsolator.clearIsolation();

      torbutton_log(3, "New Identity: Sending NEWNYM");
      // We only support TBB for newnym.
      if (
        !m_tb_control_pass ||
        (!m_tb_control_ipc_file && !m_tb_control_port)
      ) {
        const warning = torbutton_get_property_string(
          "torbutton.popup.no_newnym"
        );
        torbutton_log(
          5,
          "Torbutton cannot safely newnym. It does not have access to the Tor Control Port."
        );
        window.alert(warning);
      } else {
        const warning = torbutton_get_property_string(
          "torbutton.popup.no_newnym"
        );
        torbutton_send_ctrl_cmd("SIGNAL NEWNYM")
          .then(res => {
            if (!res) {
              torbutton_log(
                5,
                "Torbutton was unable to request a new circuit from Tor"
              );
              window.alert(warning);
            }
          })
          .catch(e => {
            torbutton_log(
              5,
              "Torbutton was unable to request a new circuit from Tor " + e
            );
            window.alert(warning);
          });
      }
    },
  };

  // Bug 1506 P2-P4: This code sets some version variables that are irrelevant.
  // It does read out some important environment variables, though. It is
  // called once per browser window.. This might belong in a component.
@@ -258,8 +192,6 @@ var torbutton_new_circuit;
      true
    );

    torbutton_new_identity_observers.register();

    torbutton_log(3, "init completed");
  };

@@ -387,23 +319,6 @@ var torbutton_new_circuit;
    gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
  };

  /* Called when we switch the use_nontor_proxy pref in either direction.
   *
   * Enables/disables domain isolation and then does new identity
   */
  function torbutton_use_nontor_proxy() {
    let domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService(
      Ci.nsISupports
    ).wrappedJSObject;

    if (m_tb_prefs.getBoolPref("extensions.torbutton.use_nontor_proxy")) {
      // Disable domain isolation
      domainIsolator.disableIsolation();
    } else {
      domainIsolator.enableIsolation();
    }
  }

  async function torbutton_do_tor_check() {
    let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"].getService(
      Ci.nsISupports
+0 −312
Original line number Diff line number Diff line
// # 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.

// ### Abbreviations

const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
  "resource://gre/modules/XPCOMUtils.jsm"
);

XPCOMUtils.defineLazyModuleGetters(this, {
  ComponentUtils: "resource://gre/modules/ComponentUtils.jsm",
});

// Make the logger available.
let logger = Cc["@torproject.org/torbutton-logger;1"].getService(Ci.nsISupports)
  .wrappedJSObject;

// Import crypto object (FF 37+).
Cu.importGlobalProperties(["crypto"]);

// ## mozilla namespace.
// Useful functionality for interacting with Mozilla services.
let 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.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(aChannel, aProxy, aCallback) {
      aCallback.onProxyFilterResult(filterFunction(aChannel, aProxy));
    },
  };
  mozilla.protocolProxyService.registerChannelFilter(
    proxyFilter,
    positionIndex
  );
};

// ## tor functionality.
let tor = {};

// __tor.noncesForDomains__.
// A mutable map that records what nonce we are using for each domain.
tor.noncesForDomains = new Map();

// __tor.noncesForUserContextId__.
// A mutable map that records what nonce we are using for each tab container.
tor.noncesForUserContextId = new Map();

// __tor.isolationEabled__.
// A bool that controls if we use SOCKS auth for isolation or not.
tor.isolationEnabled = true;

// __tor.unknownDirtySince__.
// Specifies when the current catch-all circuit was first used
tor.unknownDirtySince = Date.now();

tor.passwordForDomainAndUserContextId = function(
  domain,
  userContextId,
  create
) {
  // Check if we already have a nonce. If not, possibly create one for this
  // domain and userContextId.
  if (!tor.noncesForDomains.has(domain)) {
    if (!create) {
      return null;
    }
    tor.noncesForDomains.set(domain, tor.nonce());
  }
  if (!tor.noncesForUserContextId.has(userContextId)) {
    if (!create) {
      return null;
    }
    tor.noncesForUserContextId.set(userContextId, tor.nonce());
  }
  return (
    tor.noncesForDomains.get(domain) +
    tor.noncesForUserContextId.get(userContextId)
  );
};

tor.usernameForDomainAndUserContextId = function(domain, userContextId) {
  return `${domain}:${userContextId}`;
};

// __tor.socksProxyCredentials(originalProxy, domain, userContextId)__.
// 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 userContextId, and the password is a nonce.
tor.socksProxyCredentials = function(originalProxy, domain, userContextId) {
  let proxy = originalProxy.QueryInterface(Ci.nsIProxyInfo);
  let proxyUsername = tor.usernameForDomainAndUserContextId(
    domain,
    userContextId
  );
  let proxyPassword = tor.passwordForDomainAndUserContextId(
    domain,
    userContextId,
    true
  );
  return mozilla.protocolProxyService.newProxyInfoWithAuth(
    "socks",
    proxy.host,
    proxy.port,
    proxyUsername,
    proxyPassword,
    "", // aProxyAuthorizationHeader
    "", // aConnectionIsolationKey
    proxy.flags,
    proxy.failoverTimeout,
    proxy.failoverProxy
  );
};

tor.nonce = function() {
  // Generate a new 128 bit random tag.  Strictly speaking both using a
  // cryptographic entropy source and using 128 bits of entropy for the
  // tag are likely overkill, as correct behavior only depends on how
  // unlikely it is for there to be a collision.
  let tag = new Uint8Array(16);
  crypto.getRandomValues(tag);

  // Convert the tag to a hex string.
  let tagStr = "";
  for (let i = 0; i < tag.length; i++) {
    tagStr += (tag[i] >>> 4).toString(16);
    tagStr += (tag[i] & 0x0f).toString(16);
  }

  return tagStr;
};

tor.newCircuitForDomain = function(domain) {
  // Re-generate the nonce for the domain.
  if (domain === "") {
    domain = "--unknown--";
  }
  tor.noncesForDomains.set(domain, tor.nonce());
  logger.eclog(
    3,
    `New domain isolation for ${domain}: ${tor.noncesForDomains.get(domain)}`
  );
};

tor.newCircuitForUserContextId = function(userContextId) {
  // Re-generate the nonce for the context.
  tor.noncesForUserContextId.set(userContextId, tor.nonce());
  logger.eclog(
    3,
    `New container isolation for ${userContextId}: ${tor.noncesForUserContextId.get(
      userContextId
    )}`
  );
};

// __tor.clearIsolation()_.
// Clear the isolation state cache, forcing new circuits to be used for all
// subsequent requests.
tor.clearIsolation = function() {
  // Per-domain and per contextId nonces are stored in maps, so simply clear them.
  tor.noncesForDomains.clear();
  tor.noncesForUserContextId.clear();

  // Force a rotation on the next catch-all circuit use by setting the creation
  // time to the epoch.
  tor.unknownDirtySince = 0;
};

// __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 userContextId) and a nonce password. Tor provides a separate circuit for each
// username+password combination.
tor.isolateCircuitsByDomain = function() {
  mozilla.registerProxyChannelFilter(function(aChannel, aProxy) {
    if (!tor.isolationEnabled) {
      return aProxy;
    }
    try {
      let channel = aChannel.QueryInterface(Ci.nsIChannel),
        firstPartyDomain = channel.loadInfo.originAttributes.firstPartyDomain,
        userContextId = channel.loadInfo.originAttributes.userContextId;
      if (firstPartyDomain === "") {
        firstPartyDomain = "--unknown--";
        if (Date.now() - tor.unknownDirtySince > 1000 * 10 * 60) {
          logger.eclog(
            3,
            "tor catchall circuit has been dirty for over 10 minutes. Rotating."
          );
          tor.newCircuitForDomain("--unknown--");
          tor.unknownDirtySince = Date.now();
        }
      }
      let replacementProxy = tor.socksProxyCredentials(
        aProxy,
        firstPartyDomain,
        userContextId
      );
      logger.eclog(
        3,
        `tor SOCKS: ${channel.URI.spec} via
                       ${replacementProxy.username}:${replacementProxy.password}`
      );
      return replacementProxy;
    } catch (e) {
      logger.eclog(4, `tor domain isolator error: ${e.message}`);
      return null;
    }
  }, 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");

// DomainIsolator object.
function DomainIsolator() {
  this.wrappedJSObject = this;
}

// Firefox component requirements
DomainIsolator.prototype = {
  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
  classDescription: kMODULE_NAME,
  classID: kMODULE_CID,
  contractID: kMODULE_CONTRACTID,
  observe(subject, topic, data) {
    if (topic === "profile-after-change") {
      logger.eclog(3, "domain isolator: set up isolating circuits by domain");

      if (Services.prefs.getBoolPref("extensions.torbutton.use_nontor_proxy")) {
        tor.isolationEnabled = false;
      }
      tor.isolateCircuitsByDomain();
    }
  },

  newCircuitForDomain(domain) {
    tor.newCircuitForDomain(domain);
  },

  /**
   * Return the stored SOCKS proxy username and password for the given domain
   * and user context ID.
   *
   * @param {string} firstPartyDomain - The domain to lookup credentials for.
   * @param {integer} userContextId - The ID for the user context.
   *
   * @return {{ username: string, password: string }?} - The SOCKS credentials,
   *   or null if none are found.
   */
  getSocksProxyCredentials(firstPartyDomain, userContextId) {
    if (firstPartyDomain == "") {
      firstPartyDomain = "--unknown--";
    }
    let proxyPassword = tor.passwordForDomainAndUserContextId(
      firstPartyDomain,
      userContextId,
      // Do not create a new entry if it does not exist.
      false
    );
    if (!proxyPassword) {
      return null;
    }
    return {
      username: tor.usernameForDomainAndUserContextId(
        firstPartyDomain,
        userContextId
      ),
      password: proxyPassword,
    };
  },

  enableIsolation() {
    tor.isolationEnabled = true;
  },

  disableIsolation() {
    tor.isolationEnabled = false;
  },

  clearIsolation() {
    tor.clearIsolation();
  },

  wrappedJSObject: null,
};

// Assign factory to global object.
const NSGetFactory = XPCOMUtils.generateNSGetFactory
  ? XPCOMUtils.generateNSGetFactory([DomainIsolator])
  : ComponentUtils.generateNSGetFactory([DomainIsolator]);
+0 −4
Original line number Diff line number Diff line
@@ -43,9 +43,5 @@ torbutton.jar:
% 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 StartupObserver @torproject.org/startup-observer;1
% category profile-after-change DomainIsolator @torproject.org/domain-isolator;1
% category profile-after-change DragDropFilter @torproject.org/torbutton-dragDropFilter;1