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());