ext-pkcs11.js (7366B)
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 file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 ChromeUtils.defineESModuleGetters(this, { 8 NativeManifests: "resource://gre/modules/NativeManifests.sys.mjs", 9 ctypes: "resource://gre/modules/ctypes.sys.mjs", 10 }); 11 12 XPCOMUtils.defineLazyServiceGetter( 13 this, 14 "pkcs11db", 15 "@mozilla.org/security/pkcs11moduledb;1", 16 Ci.nsIPKCS11ModuleDB 17 ); 18 19 // eslint-disable-next-line mozilla/reject-importGlobalProperties 20 Cu.importGlobalProperties(["PathUtils"]); 21 22 var { DefaultMap } = ExtensionUtils; 23 24 const findModuleByPath = function (path) { 25 for (let module of pkcs11db.listModules()) { 26 if (module && module.libName === path) { 27 return module; 28 } 29 } 30 return null; 31 }; 32 33 this.pkcs11 = class extends ExtensionAPI { 34 getAPI(context) { 35 let manifestCache = new DefaultMap(async name => { 36 let hostInfo = await NativeManifests.lookupManifest( 37 "pkcs11", 38 name, 39 context 40 ); 41 if (hostInfo) { 42 // We don't normalize the absolute path below because 43 // `Path.normalize` throws when the target file doesn't 44 // exist, and that might be the case on non Windows 45 // builds. 46 let absolutePath = PathUtils.isAbsolute(hostInfo.manifest.path) 47 ? hostInfo.manifest.path 48 : PathUtils.joinRelative( 49 PathUtils.parent(hostInfo.path), 50 hostInfo.manifest.path 51 ); 52 53 if (AppConstants.platform === "win") { 54 // On Windows, `hostInfo.manifest.path` is expected to be a normalized 55 // absolute path. On other platforms, this path may be relative but we 56 // cannot use `PathUtils.normalize()` on non-absolute paths. 57 absolutePath = PathUtils.normalize(absolutePath); 58 hostInfo.manifest.path = absolutePath; 59 } 60 61 // PathUtils.filename throws if the path is not an absolute path. 62 // The result is expected to be the basename of the file (without 63 // the dir path and the extension) so it is fine to use an absolute 64 // path that may not be normalized (non-Windows platforms). 65 let manifestLib = PathUtils.filename(absolutePath); 66 67 if (AppConstants.platform !== "linux") { 68 manifestLib = manifestLib.toLowerCase(manifestLib); 69 } 70 if ( 71 manifestLib !== ctypes.libraryName("nssckbi") && 72 manifestLib !== ctypes.libraryName("osclientcerts") && 73 manifestLib !== ctypes.libraryName("ipcclientcerts") 74 ) { 75 return hostInfo.manifest; 76 } 77 } 78 return Promise.reject({ message: `No such PKCS#11 module ${name}` }); 79 }); 80 return { 81 pkcs11: { 82 /** 83 * Verify whether a given PKCS#11 module is installed. 84 * 85 * @param {string} name The name of the module, as specified in 86 * the manifest file. 87 * @returns {Promise} A Promise that resolves to true if the package 88 * is installed, or false if it is not. May be 89 * rejected if the module could not be found. 90 */ 91 async isModuleInstalled(name) { 92 let manifest = await manifestCache.get(name); 93 return findModuleByPath(manifest.path) !== null; 94 }, 95 /** 96 * Install a PKCS#11 module 97 * 98 * @param {string} name The name of the module, as specified in 99 * the manifest file. 100 * @param {integer} [flags = 0] Any flags to be passed on to the 101 * nsIPKCS11ModuleDB.addModule method 102 * @returns {Promise} When the Promise resolves, the module will have 103 * been installed. When it is rejected, the module 104 * either is already installed or could not be 105 * installed for some reason. 106 */ 107 async installModule(name, flags = 0) { 108 let manifest = await manifestCache.get(name); 109 if (!manifest.description) { 110 return Promise.reject({ 111 message: `The description field in the manifest for PKCS#11 module ${name} must have a value`, 112 }); 113 } 114 pkcs11db.addModule(manifest.description, manifest.path, flags, 0); 115 }, 116 /** 117 * Uninstall a PKCS#11 module 118 * 119 * @param {string} name The name of the module, as specified in 120 * the manifest file. 121 * @returns {Promise}. When the Promise resolves, the module will have 122 * been uninstalled. When it is rejected, the 123 * module either was not installed or could not be 124 * uninstalled for some reason. 125 */ 126 async uninstallModule(name) { 127 let manifest = await manifestCache.get(name); 128 let module = findModuleByPath(manifest.path); 129 if (!module) { 130 return Promise.reject({ 131 message: `The PKCS#11 module ${name} is not loaded`, 132 }); 133 } 134 pkcs11db.deleteModule(module.name); 135 }, 136 /** 137 * Get a list of slots for a given PKCS#11 module, with 138 * information on the token (if any) in the slot. 139 * 140 * The PKCS#11 standard defines slots as an abstract concept 141 * that may or may not have at most one token. In practice, when 142 * using PKCS#11 for smartcards (the most likely use case of 143 * PKCS#11 for Firefox), a slot corresponds to a cardreader, and 144 * a token corresponds to a card. 145 * 146 * @param {string} name The name of the PKCS#11 module, as 147 * specified in the manifest file. 148 * @returns {Promise} A promise that resolves to an array of objects 149 * with two properties. The `name` object contains 150 * the name of the slot; the `token` object is null 151 * if there is no token in the slot, or is an object 152 * describing various properties of the token if 153 * there is. 154 */ 155 async getModuleSlots(name) { 156 let manifest = await manifestCache.get(name); 157 let module = findModuleByPath(manifest.path); 158 if (!module) { 159 return Promise.reject({ 160 message: `The module ${name} is not installed`, 161 }); 162 } 163 let rv = []; 164 for (let slot of module.listSlots()) { 165 let token = slot.getToken(); 166 let slotobj = { 167 name: slot.name, 168 token: null, 169 }; 170 if (slot.status != 1 /* SLOT_NOT_PRESENT */) { 171 slotobj.token = { 172 name: token.tokenName, 173 manufacturer: token.tokenManID, 174 HWVersion: token.tokenHWVersion, 175 FWVersion: token.tokenFWVersion, 176 serial: token.tokenSerialNumber, 177 isLoggedIn: token.isLoggedIn(), 178 }; 179 } 180 rv.push(slotobj); 181 } 182 return rv; 183 }, 184 }, 185 }; 186 } 187 };