/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const nsIFilePicker = Components.interfaces.nsIFilePicker; const nsFilePicker = "@mozilla.org/filepicker;1"; const nsIPKCS11Slot = Components.interfaces.nsIPKCS11Slot; const nsIPKCS11Module = Components.interfaces.nsIPKCS11Module; const nsPKCS11ModuleDB = "@mozilla.org/security/pkcs11moduledb;1"; const nsIPKCS11ModuleDB = Components.interfaces.nsIPKCS11ModuleDB; const nsIPK11Token = Components.interfaces.nsIPK11Token; const nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1"; const nsIPK11TokenDB = Components.interfaces.nsIPK11TokenDB; const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock; const nsDialogParamBlock = "@mozilla.org/embedcomp/dialogparam;1"; const nsIPKCS11 = Components.interfaces.nsIPKCS11; const nsPKCS11ContractID = "@mozilla.org/security/pkcs11;1"; var { Services } = Components.utils.import("resource://gre/modules/Services.jsm", {}); var bundle; var secmoddb; var skip_enable_buttons = false; var smartCardObserver = { observe: function() { onSmartCardChange(); } }; function DeregisterSmartCardObservers() { Services.obs.removeObserver(smartCardObserver, "smartcard-insert"); Services.obs.removeObserver(smartCardObserver, "smartcard-remove"); } /* Do the initial load of all PKCS# modules and list them. */ function LoadModules() { bundle = document.getElementById("pippki_bundle"); secmoddb = Components.classes[nsPKCS11ModuleDB].getService(nsIPKCS11ModuleDB); Services.obs.addObserver(smartCardObserver, "smartcard-insert", false); Services.obs.addObserver(smartCardObserver, "smartcard-remove", false); RefreshDeviceList(); } function getPKCS11() { return Components.classes[nsPKCS11ContractID].getService(nsIPKCS11); } function getNSSString(name) { return document.getElementById("pipnss_bundle").getString(name); } function doPrompt(msg) { let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. getService(Components.interfaces.nsIPromptService); prompts.alert(window, null, msg); } function doConfirm(msg) { let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. getService(Components.interfaces.nsIPromptService); return prompts.confirm(window, null, msg); } function RefreshDeviceList() { let modules = secmoddb.listModules(); while (modules.hasMoreElements()) { let module = modules.getNext().QueryInterface(nsIPKCS11Module); let slotnames = []; let slots = module.listSlots(); while (slots.hasMoreElements()) { let slot = slots.getNext().QueryInterface(nsIPKCS11Slot); // Token names are preferred because NSS prefers lookup by token name. slotnames.push(slot.tokenName ? slot.tokenName : slot.name); } AddModule(module.name, slotnames); } // Set the text on the FIPS button. SetFIPSButton(); } function SetFIPSButton() { var fipsButton = document.getElementById("fipsbutton"); var label; if (secmoddb.isFIPSEnabled) { label = bundle.getString("disable_fips"); } else { label = bundle.getString("enable_fips"); } fipsButton.setAttribute("label", label); var can_toggle = secmoddb.canToggleFIPS; if (can_toggle) { fipsButton.removeAttribute("disabled"); } else { fipsButton.setAttribute("disabled", "true"); } } /* Add a module to the tree. slots is the array of slots in the module, * to be represented as children. */ function AddModule(module, slots) { var tree = document.getElementById("device_list"); var item = document.createElement("treeitem"); var row = document.createElement("treerow"); var cell = document.createElement("treecell"); cell.setAttribute("label", module); row.appendChild(cell); item.appendChild(row); var parent = document.createElement("treechildren"); for (let slot of slots) { var child_item = document.createElement("treeitem"); var child_row = document.createElement("treerow"); var child_cell = document.createElement("treecell"); child_cell.setAttribute("label", slot); child_row.appendChild(child_cell); child_item.appendChild(child_row); child_item.setAttribute("pk11kind", "slot"); parent.appendChild(child_item); } item.appendChild(parent); item.setAttribute("pk11kind", "module"); item.setAttribute("open", "true"); item.setAttribute("container", "true"); tree.appendChild(item); } var selected_slot; var selected_module; /* get the slot selected by the user (can only be one-at-a-time) */ function getSelectedItem() { var tree = document.getElementById('device_tree'); if (tree.currentIndex < 0) return; var item = tree.contentView.getItemAtIndex(tree.currentIndex); selected_slot = null; selected_module = null; if (item) { var kind = item.getAttribute("pk11kind"); var module_name; if (kind == "slot") { // get the module cell for this slot cell var cell = item.parentNode.parentNode.firstChild.firstChild; module_name = cell.getAttribute("label"); var module = secmoddb.findModuleByName(module_name); // get the cell for the selected row (the slot to display) cell = item.firstChild.firstChild; var slot_name = cell.getAttribute("label"); selected_slot = module.findSlotByName(slot_name); } else { // (kind == "module") // get the cell for the selected row (the module to display) cell = item.firstChild.firstChild; module_name = cell.getAttribute("label"); selected_module = secmoddb.findModuleByName(module_name); } } } function enableButtons() { if (skip_enable_buttons) { return; } var login_toggle = "true"; var logout_toggle = "true"; var pw_toggle = "true"; var unload_toggle = "true"; getSelectedItem(); if (selected_module) { unload_toggle = "false"; showModuleInfo(); } else if (selected_slot) { // here's the workaround - login functions are all with token, // so grab the token type var selected_token = selected_slot.getToken(); if (selected_token != null) { if (selected_token.needsLogin() || !(selected_token.needsUserInit)) { pw_toggle = "false"; if (selected_token.needsLogin()) { if (selected_token.isLoggedIn()) { logout_toggle = "false"; } else { login_toggle = "false"; } } } } showSlotInfo(); } var thebutton = document.getElementById('login_button'); thebutton.setAttribute("disabled", login_toggle); thebutton = document.getElementById('logout_button'); thebutton.setAttribute("disabled", logout_toggle); thebutton = document.getElementById('change_pw_button'); thebutton.setAttribute("disabled", pw_toggle); thebutton = document.getElementById('unload_button'); thebutton.setAttribute("disabled", unload_toggle); // not implemented //thebutton = document.getElementById('change_slotname_button'); //thebutton.setAttribute("disabled", toggle); } // clear the display of information for the slot function ClearInfoList() { let infoList = document.getElementById("info_list"); while (infoList.hasChildNodes()) { infoList.removeChild(infoList.firstChild); } } function ClearDeviceList() { ClearInfoList(); skip_enable_buttons = true; var tree = document.getElementById('device_tree'); tree.view.selection.clearSelection(); skip_enable_buttons = false; // Remove the existing listed modules so that a refresh doesn't display the // module that just changed. let deviceList = document.getElementById("device_list"); while (deviceList.hasChildNodes()) { deviceList.removeChild(deviceList.firstChild); } } // show a list of info about a slot function showSlotInfo() { var present = true; ClearInfoList(); switch (selected_slot.status) { case nsIPKCS11Slot.SLOT_DISABLED: AddInfoRow(bundle.getString("devinfo_status"), bundle.getString("devinfo_stat_disabled"), "tok_status"); present = false; break; case nsIPKCS11Slot.SLOT_NOT_PRESENT: AddInfoRow(bundle.getString("devinfo_status"), bundle.getString("devinfo_stat_notpresent"), "tok_status"); present = false; break; case nsIPKCS11Slot.SLOT_UNINITIALIZED: AddInfoRow(bundle.getString("devinfo_status"), bundle.getString("devinfo_stat_uninitialized"), "tok_status"); break; case nsIPKCS11Slot.SLOT_NOT_LOGGED_IN: AddInfoRow(bundle.getString("devinfo_status"), bundle.getString("devinfo_stat_notloggedin"), "tok_status"); break; case nsIPKCS11Slot.SLOT_LOGGED_IN: AddInfoRow(bundle.getString("devinfo_status"), bundle.getString("devinfo_stat_loggedin"), "tok_status"); break; case nsIPKCS11Slot.SLOT_READY: AddInfoRow(bundle.getString("devinfo_status"), bundle.getString("devinfo_stat_ready"), "tok_status"); break; } AddInfoRow(bundle.getString("devinfo_desc"), selected_slot.desc, "slot_desc"); AddInfoRow(bundle.getString("devinfo_manID"), selected_slot.manID, "slot_manID"); AddInfoRow(bundle.getString("devinfo_hwversion"), selected_slot.HWVersion, "slot_hwv"); AddInfoRow(bundle.getString("devinfo_fwversion"), selected_slot.FWVersion, "slot_fwv"); if (present) { showTokenInfo(); } } function showModuleInfo() { ClearInfoList(); AddInfoRow(bundle.getString("devinfo_modname"), selected_module.name, "module_name"); AddInfoRow(bundle.getString("devinfo_modpath"), selected_module.libName, "module_path"); } // add a row to the info list, as [col1 col2] (ex.: ["status" "logged in"]) function AddInfoRow(col1, col2, cell_id) { var tree = document.getElementById("info_list"); var item = document.createElement("treeitem"); var row = document.createElement("treerow"); var cell1 = document.createElement("treecell"); cell1.setAttribute("label", col1); cell1.setAttribute("crop", "never"); row.appendChild(cell1); var cell2 = document.createElement("treecell"); cell2.setAttribute("label", col2); cell2.setAttribute("crop", "never"); cell2.setAttribute("id", cell_id); row.appendChild(cell2); item.appendChild(row); tree.appendChild(item); } // log in to a slot function doLogin() { getSelectedItem(); // here's the workaround - login functions are with token var selected_token = selected_slot.getToken(); try { selected_token.login(false); var tok_status = document.getElementById("tok_status"); if (selected_token.isLoggedIn()) { tok_status.setAttribute("label", bundle.getString("devinfo_stat_loggedin")); } else { tok_status.setAttribute("label", bundle.getString("devinfo_stat_notloggedin")); } } catch (e) { doPrompt(bundle.getString("login_failed")); } enableButtons(); } // log out of a slot function doLogout() { getSelectedItem(); // here's the workaround - login functions are with token var selected_token = selected_slot.getToken(); try { selected_token.logoutAndDropAuthenticatedResources(); var tok_status = document.getElementById("tok_status"); if (selected_token.isLoggedIn()) { tok_status.setAttribute("label", bundle.getString("devinfo_stat_loggedin")); } else { tok_status.setAttribute("label", bundle.getString("devinfo_stat_notloggedin")); } } catch (e) { } enableButtons(); } // load a new device function doLoad() { window.open("load_device.xul", "loaddevice", "chrome,centerscreen,modal"); ClearDeviceList(); RefreshDeviceList(); } function deleteSelected() { getSelectedItem(); if (selected_module && doConfirm(getNSSString("DelModuleWarning"))) { try { getPKCS11().deleteModule(selected_module.name); } catch (e) { doPrompt(getNSSString("DelModuleError")); return false; } selected_module = null; return true; } return false; } function doUnload() { if (deleteSelected()) { ClearDeviceList(); RefreshDeviceList(); } } // handle card insertion and removal function onSmartCardChange() { var tree = document.getElementById('device_tree'); var index = tree.currentIndex; tree.currentIndex = 0; ClearDeviceList(); RefreshDeviceList(); tree.currentIndex = index; enableButtons(); } function changePassword() { getSelectedItem(); let params = Components.classes[nsDialogParamBlock] .createInstance(nsIDialogParamBlock); params.SetString(1, selected_slot.tokenName); window.openDialog("changepassword.xul", "", "chrome,centerscreen,modal", params); showSlotInfo(); enableButtons(); } // browse fs for PKCS#11 device function doBrowseFiles() { var srbundle = document.getElementById("pippki_bundle"); var fp = Components.classes[nsFilePicker].createInstance(nsIFilePicker); fp.init(window, srbundle.getString("loadPK11TokenDialog"), nsIFilePicker.modeOpen); fp.appendFilters(nsIFilePicker.filterAll); if (fp.show() == nsIFilePicker.returnOK) { var pathbox = document.getElementById("device_path"); pathbox.setAttribute("value", fp.file.path); } } function doLoadDevice() { var name_box = document.getElementById("device_name"); var path_box = document.getElementById("device_path"); try { getPKCS11().addModule(name_box.value, path_box.value, 0, 0); } catch (e) { if (e.result == Components.results.NS_ERROR_ILLEGAL_VALUE) { doPrompt(getNSSString("AddModuleDup")); } else { doPrompt(getNSSString("AddModuleFailure")); } return false; } return true; } // ------------------------------------- Old code function showTokenInfo() { //ClearInfoList(); var selected_token = selected_slot.getToken(); AddInfoRow(bundle.getString("devinfo_label"), selected_token.tokenLabel, "tok_label"); AddInfoRow(bundle.getString("devinfo_manID"), selected_token.tokenManID, "tok_manID"); AddInfoRow(bundle.getString("devinfo_serialnum"), selected_token.tokenSerialNumber, "tok_sNum"); AddInfoRow(bundle.getString("devinfo_hwversion"), selected_token.tokenHWVersion, "tok_hwv"); AddInfoRow(bundle.getString("devinfo_fwversion"), selected_token.tokenFWVersion, "tok_fwv"); } function toggleFIPS() { if (!secmoddb.isFIPSEnabled) { // A restriction of FIPS mode is, the password must be set // In FIPS mode the password must be non-empty. // This is different from what we allow in NON-Fips mode. var tokendb = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB); var internal_token = tokendb.getInternalKeyToken(); // nsIPK11Token var slot = secmoddb.findSlotByName(internal_token.tokenName); switch (slot.status) { case nsIPKCS11Slot.SLOT_UNINITIALIZED: case nsIPKCS11Slot.SLOT_READY: // Token has either no or an empty password. doPrompt(bundle.getString("fips_nonempty_password_required")); return; } } try { secmoddb.toggleFIPSMode(); } catch (e) { doPrompt(bundle.getString("unable_to_toggle_fips")); return; } // Remove the existing listed modules so that a refresh doesn't display the // module that just changed. ClearDeviceList(); RefreshDeviceList(); }