tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

exceptionDialog.js (10735B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const { setText, viewCertHelper, checkCertHelper } = ChromeUtils.importESModule(
      8  "resource://gre/modules/psm/pippki.sys.mjs"
      9 );
     10 
     11 var gDialog;
     12 var gSecInfo;
     13 var gCert;
     14 var gChecking;
     15 var gBroken;
     16 var gNeedReset;
     17 
     18 const { PrivateBrowsingUtils } = ChromeUtils.importESModule(
     19  "resource://gre/modules/PrivateBrowsingUtils.sys.mjs"
     20 );
     21 
     22 function initExceptionDialog() {
     23  gNeedReset = false;
     24  gDialog = document.getElementById("exceptiondialog");
     25  let warningText = document.getElementById("warningText");
     26  document.l10n.setAttributes(warningText, "add-exception-branded-warning");
     27  let confirmButton = gDialog.getButton("extra1");
     28  let l10nUpdatedElements = [confirmButton, warningText];
     29  confirmButton.disabled = true;
     30 
     31  document
     32    .getElementById("locationTextBox")
     33    .addEventListener("input", () => handleTextChange());
     34  document
     35    .getElementById("viewCertButton")
     36    .addEventListener("command", () => viewCertButtonClick());
     37 
     38  var args = window.arguments;
     39  if (args && args[0]) {
     40    if (args[0].location) {
     41      // We were pre-seeded with a location.
     42      document.getElementById("locationTextBox").value = args[0].location;
     43      document.getElementById("checkCertButton").disabled = false;
     44 
     45      if (args[0].securityInfo) {
     46        gSecInfo = args[0].securityInfo;
     47        gCert = gSecInfo.serverCert;
     48        gBroken = true;
     49        l10nUpdatedElements = l10nUpdatedElements.concat(updateCertStatus());
     50      } else if (args[0].prefetchCert) {
     51        // We can optionally pre-fetch the certificate too.  Don't do this
     52        // synchronously, since it would prevent the window from appearing
     53        // until the fetch is completed, which could be multiple seconds.
     54        // Instead, let's use a timer to spawn the actual fetch, but update
     55        // the dialog to "checking..." state right away, so that the UI
     56        // is appropriately responsive.  Bug 453855
     57        document.getElementById("checkCertButton").disabled = true;
     58        gChecking = true;
     59        l10nUpdatedElements = l10nUpdatedElements.concat(updateCertStatus());
     60 
     61        window.setTimeout(checkCert, 0);
     62      }
     63    }
     64 
     65    // Set out parameter to false by default
     66    args[0].exceptionAdded = false;
     67  }
     68 
     69  for (let id of [
     70    "warningSupplemental",
     71    "certLocationLabel",
     72    "checkCertButton",
     73    "statusDescription",
     74    "statusLongDescription",
     75    "viewCertButton",
     76    "permanent",
     77  ]) {
     78    let element = document.getElementById(id);
     79    l10nUpdatedElements.push(element);
     80  }
     81 
     82  document.l10n
     83    .translateElements(l10nUpdatedElements)
     84    .then(() => window.sizeToContent());
     85 
     86  document.addEventListener("dialogextra1", addException);
     87  document.addEventListener("dialogextra2", checkCert);
     88 }
     89 
     90 /**
     91 * Helper function for checkCert. Set as the onerror/onload callbacks for an
     92 * XMLHttpRequest. Sets gSecInfo, gCert, gBroken, and gChecking according to
     93 * the load information from the request. Probably should not be used directly.
     94 *
     95 * @param {XMLHttpRequest} req
     96 *        The XMLHttpRequest created and sent by checkCert.
     97 * @param {Event} evt
     98 *        The load or error event.
     99 */
    100 function grabCert(req, evt) {
    101  if (req.channel && req.channel.securityInfo) {
    102    gSecInfo = req.channel.securityInfo;
    103    gCert = gSecInfo ? gSecInfo.serverCert : null;
    104  }
    105  gBroken = evt.type == "error";
    106  gChecking = false;
    107  document.l10n
    108    .translateElements(updateCertStatus())
    109    .then(() => window.sizeToContent());
    110 }
    111 
    112 /**
    113 * Attempt to download the certificate for the location specified, and populate
    114 * the Certificate Status section with the result.
    115 */
    116 async function checkCert() {
    117  gCert = null;
    118  gSecInfo = null;
    119  gChecking = true;
    120  gBroken = false;
    121  await document.l10n.translateElements(updateCertStatus());
    122  window.sizeToContent();
    123 
    124  let uri = getURI();
    125 
    126  if (uri) {
    127    checkCertHelper(uri, grabCert);
    128  } else {
    129    gChecking = false;
    130    await document.l10n.translateElements(updateCertStatus());
    131    window.sizeToContent();
    132  }
    133 }
    134 
    135 /**
    136 * Build and return a URI, based on the information supplied in the
    137 * Certificate Location fields
    138 *
    139 * @returns {nsIURI}
    140 *          URI constructed from the information supplied on success, null
    141 *          otherwise.
    142 */
    143 function getURI() {
    144  // Use fixup service instead of just ioservice's newURI since it's quite
    145  // likely that the host will be supplied without a protocol prefix, resulting
    146  // in malformed uri exceptions being thrown.
    147  let locationTextBox = document.getElementById("locationTextBox");
    148  let { preferredURI: uri } = Services.uriFixup.getFixupURIInfo(
    149    locationTextBox.value
    150  );
    151 
    152  if (!uri) {
    153    return null;
    154  }
    155 
    156  let mutator = uri.mutate();
    157  if (uri.scheme == "http") {
    158    mutator.setScheme("https");
    159  }
    160 
    161  if (uri.port == -1) {
    162    mutator.setPort(443);
    163  }
    164 
    165  return mutator.finalize();
    166 }
    167 
    168 function resetDialog() {
    169  document.getElementById("viewCertButton").disabled = true;
    170  document.getElementById("permanent").disabled = true;
    171  gDialog.getButton("extra1").disabled = true;
    172  setText(document, "headerDescription", "");
    173  setText(document, "statusDescription", "");
    174  setText(document, "statusLongDescription", "");
    175  setText(document, "status2Description", "");
    176  setText(document, "status2LongDescription", "");
    177  setText(document, "status3Description", "");
    178  setText(document, "status3LongDescription", "");
    179  window.sizeToContent();
    180 }
    181 
    182 /**
    183 * Called by input textboxes to manage UI state
    184 */
    185 function handleTextChange() {
    186  var checkCertButton = document.getElementById("checkCertButton");
    187  checkCertButton.disabled = !document.getElementById("locationTextBox").value;
    188  if (gNeedReset) {
    189    gNeedReset = false;
    190    resetDialog();
    191  }
    192 }
    193 
    194 function updateCertStatus() {
    195  var shortDesc, longDesc;
    196  let l10nUpdatedElements = [];
    197  if (gCert) {
    198    if (gBroken) {
    199      var mms = "add-exception-domain-mismatch-short";
    200      var mml = "add-exception-domain-mismatch-long";
    201      var exs = "add-exception-expired-short";
    202      var exl = "add-exception-expired-long";
    203      var uts = "add-exception-unverified-or-bad-signature-short";
    204      var utl = "add-exception-unverified-or-bad-signature-long";
    205      if (
    206        gSecInfo.overridableErrorCategory ==
    207        Ci.nsITransportSecurityInfo.ERROR_TRUST
    208      ) {
    209        shortDesc = uts;
    210        longDesc = utl;
    211      } else if (
    212        gSecInfo.overridableErrorCategory ==
    213        Ci.nsITransportSecurityInfo.ERROR_DOMAIN
    214      ) {
    215        shortDesc = mms;
    216        longDesc = mml;
    217      } else if (
    218        gSecInfo.overridableErrorCategory ==
    219        Ci.nsITransportSecurityInfo.ERROR_TIME
    220      ) {
    221        shortDesc = exs;
    222        longDesc = exl;
    223      }
    224      // In these cases, we do want to enable the "Add Exception" button
    225      gDialog.getButton("extra1").disabled = false;
    226 
    227      // If the Private Browsing service is available and the mode is active,
    228      // don't store permanent exceptions, since they would persist after
    229      // private browsing mode was disabled.
    230      var inPrivateBrowsing = inPrivateBrowsingMode();
    231      var pe = document.getElementById("permanent");
    232      pe.disabled = inPrivateBrowsing;
    233      pe.checked = !inPrivateBrowsing;
    234 
    235      let headerDescription = document.getElementById("headerDescription");
    236      document.l10n.setAttributes(
    237        headerDescription,
    238        "add-exception-invalid-header"
    239      );
    240      l10nUpdatedElements.push(headerDescription);
    241    } else {
    242      shortDesc = "add-exception-valid-short";
    243      longDesc = "add-exception-valid-long";
    244      gDialog.getButton("extra1").disabled = true;
    245      document.getElementById("permanent").disabled = true;
    246    }
    247 
    248    // We're done checking the certificate, so allow the user to check it again.
    249    document.getElementById("checkCertButton").disabled = false;
    250    document.getElementById("viewCertButton").disabled = false;
    251 
    252    // Notify observers about the availability of the certificate
    253    Services.obs.notifyObservers(window, "cert-exception-ui-ready");
    254  } else if (gChecking) {
    255    shortDesc = "add-exception-checking-short";
    256    longDesc = "add-exception-checking-long";
    257    // We're checking the certificate, so we disable the Get Certificate
    258    // button to make sure that the user can't interrupt the process and
    259    // trigger another certificate fetch.
    260    document.getElementById("checkCertButton").disabled = true;
    261    document.getElementById("viewCertButton").disabled = true;
    262    gDialog.getButton("extra1").disabled = true;
    263    document.getElementById("permanent").disabled = true;
    264  } else {
    265    shortDesc = "add-exception-no-cert-short";
    266    longDesc = "add-exception-no-cert-long";
    267    // We're done checking the certificate, so allow the user to check it again.
    268    document.getElementById("checkCertButton").disabled = false;
    269    document.getElementById("viewCertButton").disabled = true;
    270    gDialog.getButton("extra1").disabled = true;
    271    document.getElementById("permanent").disabled = true;
    272  }
    273  let statusDescription = document.getElementById("statusDescription");
    274  let statusLongDescription = document.getElementById("statusLongDescription");
    275  document.l10n.setAttributes(statusDescription, shortDesc);
    276  document.l10n.setAttributes(statusLongDescription, longDesc);
    277  l10nUpdatedElements.push(statusDescription);
    278  l10nUpdatedElements.push(statusLongDescription);
    279 
    280  gNeedReset = true;
    281  return l10nUpdatedElements;
    282 }
    283 
    284 /**
    285 * Handle user request to display certificate details
    286 */
    287 function viewCertButtonClick() {
    288  if (gCert) {
    289    viewCertHelper(this, gCert);
    290  }
    291 }
    292 
    293 /**
    294 * Handle user request to add an exception for the specified cert
    295 */
    296 function addException() {
    297  if (!gCert || !gSecInfo) {
    298    return;
    299  }
    300 
    301  var overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(
    302    Ci.nsICertOverrideService
    303  );
    304  var permanentCheckbox = document.getElementById("permanent");
    305  var shouldStorePermanently =
    306    permanentCheckbox.checked && !inPrivateBrowsingMode();
    307  var uri = getURI();
    308  overrideService.rememberValidityOverride(
    309    uri.asciiHost,
    310    uri.port,
    311    {},
    312    gCert,
    313    !shouldStorePermanently
    314  );
    315 
    316  let args = window.arguments;
    317  if (args && args[0]) {
    318    args[0].exceptionAdded = true;
    319  }
    320 
    321  gDialog.acceptDialog();
    322 }
    323 
    324 /**
    325 * @returns {boolean} Whether this dialog is in private browsing mode.
    326 */
    327 function inPrivateBrowsingMode() {
    328  return window.isChromeWindow
    329    ? PrivateBrowsingUtils.isWindowPrivate(window)
    330    : PrivateBrowsingUtils.isContentWindowPrivate(window);
    331 }
    332 
    333 window.addEventListener("load", () => initExceptionDialog());