tor-browser

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

device_manager.js (12769B)


      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 "use strict";
      5 
      6 const { XPCOMUtils } = ChromeUtils.importESModule(
      7  "resource://gre/modules/XPCOMUtils.sys.mjs"
      8 );
      9 
     10 var secmoddb;
     11 var skip_enable_buttons = false;
     12 
     13 /* Do the initial load of all PKCS# modules and list them. */
     14 function LoadModules() {
     15  secmoddb = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
     16    Ci.nsIPKCS11ModuleDB
     17  );
     18  RefreshDeviceList();
     19 
     20  document
     21    .getElementById("device_tree")
     22    .addEventListener("select", () => enableButtons());
     23  document
     24    .getElementById("devicemanager")
     25    .addEventListener("command", event => {
     26      switch (event.target.id) {
     27        case "login_button":
     28          doLogin();
     29          break;
     30        case "logout_button":
     31          doLogout();
     32          break;
     33        case "change_pw_button":
     34          changePassword();
     35          break;
     36        case "load_button":
     37          doLoad();
     38          break;
     39        case "unload_button":
     40          doUnload();
     41          break;
     42        case "fipsbutton":
     43          toggleFIPS();
     44          break;
     45        default:
     46          // Default means that we are not handling a command so we should
     47          // probably let people know.
     48          throw new Error("Unhandled command event");
     49      }
     50    });
     51 }
     52 
     53 async function doPrompt(l10n_id) {
     54  let [msg] = await document.l10n.formatValues([{ id: l10n_id }]);
     55  Services.prompt.alert(window, null, msg);
     56 }
     57 
     58 async function doConfirm(l10n_id) {
     59  let [msg] = await document.l10n.formatValues([{ id: l10n_id }]);
     60  return Services.prompt.confirm(window, null, msg);
     61 }
     62 
     63 function RefreshDeviceList() {
     64  for (let module of secmoddb.listModules()) {
     65    let slots = module.listSlots();
     66    AddModule(module, slots);
     67  }
     68 
     69  // Set the text on the FIPS button.
     70  SetFIPSButton();
     71 }
     72 
     73 function SetFIPSButton() {
     74  var fipsButton = document.getElementById("fipsbutton");
     75  if (secmoddb.isFIPSEnabled) {
     76    document.l10n.setAttributes(fipsButton, "devmgr-button-disable-fips");
     77  } else {
     78    document.l10n.setAttributes(fipsButton, "devmgr-button-enable-fips");
     79  }
     80 
     81  var can_toggle = secmoddb.canToggleFIPS;
     82  if (can_toggle) {
     83    fipsButton.removeAttribute("disabled");
     84  } else {
     85    fipsButton.setAttribute("disabled", "true");
     86  }
     87 }
     88 
     89 /* Add a module to the tree.  slots is the array of slots in the module,
     90 * to be represented as children.
     91 */
     92 function AddModule(module, slots) {
     93  var tree = document.getElementById("device_list");
     94  var item = document.createXULElement("treeitem");
     95  var row = document.createXULElement("treerow");
     96  var cell = document.createXULElement("treecell");
     97  cell.setAttribute("label", module.name);
     98  row.appendChild(cell);
     99  item.appendChild(row);
    100  var parent = document.createXULElement("treechildren");
    101  for (let slot of slots) {
    102    var child_item = document.createXULElement("treeitem");
    103    var child_row = document.createXULElement("treerow");
    104    var child_cell = document.createXULElement("treecell");
    105    child_cell.setAttribute("label", slot.name);
    106    child_row.appendChild(child_cell);
    107    child_item.appendChild(child_row);
    108    child_item.setAttribute("pk11kind", "slot");
    109    // 'slot' is an attribute on any HTML element, hence 'slotObject' instead.
    110    child_item.slotObject = slot;
    111    parent.appendChild(child_item);
    112  }
    113  item.appendChild(parent);
    114  item.setAttribute("pk11kind", "module");
    115  item.module = module;
    116  item.setAttribute("open", "true");
    117  item.setAttribute("container", "true");
    118  tree.appendChild(item);
    119 }
    120 
    121 var selected_slot;
    122 var selected_module;
    123 
    124 /* get the slot selected by the user (can only be one-at-a-time) */
    125 function getSelectedItem() {
    126  let tree = document.getElementById("device_tree");
    127  if (tree.currentIndex < 0) {
    128    return;
    129  }
    130  let item = tree.view.getItemAtIndex(tree.currentIndex);
    131  selected_slot = null;
    132  selected_module = null;
    133  if (item) {
    134    let kind = item.getAttribute("pk11kind");
    135    if (kind == "slot") {
    136      selected_slot = item.slotObject;
    137    } else {
    138      // (kind == "module")
    139      selected_module = item.module;
    140    }
    141  }
    142 }
    143 
    144 function enableButtons() {
    145  if (skip_enable_buttons) {
    146    return;
    147  }
    148 
    149  var login_toggle = true;
    150  var logout_toggle = true;
    151  var pw_toggle = true;
    152  var unload_toggle = true;
    153  getSelectedItem();
    154  if (selected_module) {
    155    unload_toggle = false;
    156    showModuleInfo();
    157  } else if (selected_slot) {
    158    // here's the workaround - login functions are all with token,
    159    // so grab the token type
    160    var selected_token = selected_slot.getToken();
    161    if (selected_token != null) {
    162      if (selected_token.needsLogin() || !selected_token.needsUserInit) {
    163        pw_toggle = false;
    164        if (selected_token.needsLogin()) {
    165          if (selected_token.isLoggedIn()) {
    166            logout_toggle = false;
    167          } else {
    168            login_toggle = false;
    169          }
    170        }
    171      }
    172 
    173      if (
    174        !Services.policies.isAllowed("createMasterPassword") &&
    175        selected_token.isInternalKeyToken &&
    176        !selected_token.hasPassword
    177      ) {
    178        pw_toggle = "true";
    179      }
    180    }
    181    showSlotInfo();
    182  }
    183  document
    184    .getElementById("login_button")
    185    .toggleAttribute("disabled", login_toggle);
    186  document
    187    .getElementById("logout_button")
    188    .toggleAttribute("disabled", logout_toggle);
    189  document
    190    .getElementById("change_pw_button")
    191    .toggleAttribute("disabled", pw_toggle);
    192  document
    193    .getElementById("unload_button")
    194    .toggleAttribute("disabled", unload_toggle);
    195 }
    196 
    197 // clear the display of information for the slot
    198 function ClearInfoList() {
    199  let infoList = document.getElementById("info_list");
    200  while (infoList.hasChildNodes()) {
    201    infoList.firstChild.remove();
    202  }
    203 }
    204 
    205 function ClearDeviceList() {
    206  ClearInfoList();
    207 
    208  skip_enable_buttons = true;
    209  var tree = document.getElementById("device_tree");
    210  tree.view.selection.clearSelection();
    211  skip_enable_buttons = false;
    212 
    213  // Remove the existing listed modules so that a refresh doesn't display the
    214  // module that just changed.
    215  let deviceList = document.getElementById("device_list");
    216  while (deviceList.hasChildNodes()) {
    217    deviceList.firstChild.remove();
    218  }
    219 }
    220 
    221 // show a list of info about a slot
    222 function showSlotInfo() {
    223  var present = true;
    224  ClearInfoList();
    225  switch (selected_slot.status) {
    226    case Ci.nsIPKCS11Slot.SLOT_DISABLED:
    227      AddInfoRow(
    228        "devinfo-status",
    229        { l10nID: "devinfo-status-disabled" },
    230        "tok_status"
    231      );
    232      present = false;
    233      break;
    234    case Ci.nsIPKCS11Slot.SLOT_NOT_PRESENT:
    235      AddInfoRow(
    236        "devinfo-status",
    237        { l10nID: "devinfo-status-not-present" },
    238        "tok_status"
    239      );
    240      present = false;
    241      break;
    242    case Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED:
    243      AddInfoRow(
    244        "devinfo-status",
    245        { l10nID: "devinfo-status-uninitialized" },
    246        "tok_status"
    247      );
    248      break;
    249    case Ci.nsIPKCS11Slot.SLOT_NOT_LOGGED_IN:
    250      AddInfoRow(
    251        "devinfo-status",
    252        { l10nID: "devinfo-status-not-logged-in" },
    253        "tok_status"
    254      );
    255      break;
    256    case Ci.nsIPKCS11Slot.SLOT_LOGGED_IN:
    257      AddInfoRow(
    258        "devinfo-status",
    259        { l10nID: "devinfo-status-logged-in" },
    260        "tok_status"
    261      );
    262      break;
    263    case Ci.nsIPKCS11Slot.SLOT_READY:
    264      AddInfoRow(
    265        "devinfo-status",
    266        { l10nID: "devinfo-status-ready" },
    267        "tok_status"
    268      );
    269      break;
    270    default:
    271      return;
    272  }
    273  AddInfoRow("devinfo-desc", { label: selected_slot.desc }, "slot_desc");
    274  AddInfoRow("devinfo-man-id", { label: selected_slot.manID }, "slot_manID");
    275  AddInfoRow(
    276    "devinfo-hwversion",
    277    { label: selected_slot.HWVersion },
    278    "slot_hwv"
    279  );
    280  AddInfoRow(
    281    "devinfo-fwversion",
    282    { label: selected_slot.FWVersion },
    283    "slot_fwv"
    284  );
    285  if (present) {
    286    showTokenInfo();
    287  }
    288 }
    289 
    290 function showModuleInfo() {
    291  ClearInfoList();
    292  AddInfoRow("devinfo-modname", { label: selected_module.name }, "module_name");
    293  AddInfoRow(
    294    "devinfo-modpath",
    295    { label: selected_module.libName },
    296    "module_path"
    297  );
    298 }
    299 
    300 // add a row to the info list, as [col1 col2] (ex.: ["status" "logged in"])
    301 function AddInfoRow(l10nID, col2, cell_id) {
    302  var tree = document.getElementById("info_list");
    303  var item = document.createXULElement("treeitem");
    304  var row = document.createXULElement("treerow");
    305  var cell1 = document.createXULElement("treecell");
    306  document.l10n.setAttributes(cell1, l10nID);
    307  cell1.setAttribute("crop", "never");
    308  row.appendChild(cell1);
    309  var cell2 = document.createXULElement("treecell");
    310  if (col2.l10nID) {
    311    document.l10n.setAttributes(cell2, col2.l10nID);
    312  } else {
    313    cell2.setAttribute("label", col2.label);
    314  }
    315  cell2.setAttribute("crop", "never");
    316  cell2.setAttribute("id", cell_id);
    317  row.appendChild(cell2);
    318  item.appendChild(row);
    319  tree.appendChild(item);
    320 }
    321 
    322 // log in to a slot
    323 function doLogin() {
    324  getSelectedItem();
    325  // here's the workaround - login functions are with token
    326  var selected_token = selected_slot.getToken();
    327  try {
    328    selected_token.login(false);
    329    var tok_status = document.getElementById("tok_status");
    330    if (selected_token.isLoggedIn()) {
    331      document.l10n.setAttributes(tok_status, "devinfo-status-logged-in");
    332    } else {
    333      document.l10n.setAttributes(tok_status, "devinfo-status-not-logged-in");
    334    }
    335  } catch (e) {
    336    doPrompt("login-failed");
    337  }
    338  enableButtons();
    339 }
    340 
    341 // log out of a slot
    342 function doLogout() {
    343  getSelectedItem();
    344  // here's the workaround - login functions are with token
    345  var selected_token = selected_slot.getToken();
    346  try {
    347    selected_token.logoutAndDropAuthenticatedResources();
    348    var tok_status = document.getElementById("tok_status");
    349    if (selected_token.isLoggedIn()) {
    350      document.l10n.setAttributes(tok_status, "devinfo-status-logged-in");
    351    } else {
    352      document.l10n.setAttributes(tok_status, "devinfo-status-not-logged-in");
    353    }
    354  } catch (e) {}
    355  enableButtons();
    356 }
    357 
    358 // load a new device
    359 function doLoad() {
    360  window.browsingContext.topChromeWindow.open(
    361    "load_device.xhtml",
    362    "loaddevice",
    363    "chrome,centerscreen,modal"
    364  );
    365  ClearDeviceList();
    366  RefreshDeviceList();
    367 }
    368 
    369 async function deleteSelected() {
    370  getSelectedItem();
    371  if (selected_module && (await doConfirm("del-module-warning"))) {
    372    try {
    373      secmoddb.deleteModule(selected_module.name);
    374    } catch (e) {
    375      doPrompt("del-module-error");
    376      return false;
    377    }
    378    selected_module = null;
    379    return true;
    380  }
    381  return false;
    382 }
    383 
    384 async function doUnload() {
    385  if (await deleteSelected()) {
    386    ClearDeviceList();
    387    RefreshDeviceList();
    388  }
    389 }
    390 
    391 function changePassword() {
    392  getSelectedItem();
    393  let params = Cc["@mozilla.org/embedcomp/dialogparam;1"].createInstance(
    394    Ci.nsIDialogParamBlock
    395  );
    396  let objects = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
    397  objects.appendElement(selected_slot.getToken());
    398  params.objects = objects;
    399  window.browsingContext.topChromeWindow.openDialog(
    400    "changepassword.xhtml",
    401    "",
    402    "chrome,centerscreen,modal",
    403    params
    404  );
    405  showSlotInfo();
    406  enableButtons();
    407 }
    408 
    409 // -------------------------------------   Old code
    410 
    411 function showTokenInfo() {
    412  var selected_token = selected_slot.getToken();
    413  AddInfoRow("devinfo-label", { label: selected_token.tokenName }, "tok_label");
    414  AddInfoRow(
    415    "devinfo-man-id",
    416    { label: selected_token.tokenManID },
    417    "tok_manID"
    418  );
    419  AddInfoRow(
    420    "devinfo-serialnum",
    421    { label: selected_token.tokenSerialNumber },
    422    "tok_sNum"
    423  );
    424  AddInfoRow(
    425    "devinfo-hwversion",
    426    { label: selected_token.tokenHWVersion },
    427    "tok_hwv"
    428  );
    429  AddInfoRow(
    430    "devinfo-fwversion",
    431    { label: selected_token.tokenFWVersion },
    432    "tok_fwv"
    433  );
    434 }
    435 
    436 function toggleFIPS() {
    437  if (!secmoddb.isFIPSEnabled) {
    438    // A restriction of FIPS mode is, the password must be set
    439    // In FIPS mode the password must be non-empty.
    440    // This is different from what we allow in NON-Fips mode.
    441 
    442    var tokendb = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
    443      Ci.nsIPK11TokenDB
    444    );
    445    var internal_token = tokendb.getInternalKeyToken(); // nsIPK11Token
    446    if (!internal_token.hasPassword) {
    447      // Token has either no or an empty password.
    448      doPrompt("fips-nonempty-primary-password-required");
    449      return;
    450    }
    451  }
    452 
    453  try {
    454    secmoddb.toggleFIPSMode();
    455  } catch (e) {
    456    doPrompt("unable-to-toggle-fips");
    457    return;
    458  }
    459 
    460  // Remove the existing listed modules so that a refresh doesn't display the
    461  // module that just changed.
    462  ClearDeviceList();
    463 
    464  RefreshDeviceList();
    465 }
    466 
    467 window.addEventListener("load", () => LoadModules());