summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/parent/ext-pkcs11.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/extensions/parent/ext-pkcs11.js')
-rw-r--r--browser/components/extensions/parent/ext-pkcs11.js187
1 files changed, 187 insertions, 0 deletions
diff --git a/browser/components/extensions/parent/ext-pkcs11.js b/browser/components/extensions/parent/ext-pkcs11.js
new file mode 100644
index 0000000000..696133bfc5
--- /dev/null
+++ b/browser/components/extensions/parent/ext-pkcs11.js
@@ -0,0 +1,187 @@
+/* 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";
+
+ChromeUtils.defineESModuleGetters(this, {
+ NativeManifests: "resource://gre/modules/NativeManifests.sys.mjs",
+ ctypes: "resource://gre/modules/ctypes.sys.mjs",
+});
+
+XPCOMUtils.defineLazyServiceGetter(
+ this,
+ "pkcs11db",
+ "@mozilla.org/security/pkcs11moduledb;1",
+ "nsIPKCS11ModuleDB"
+);
+
+// eslint-disable-next-line mozilla/reject-importGlobalProperties
+Cu.importGlobalProperties(["PathUtils"]);
+
+var { DefaultMap } = ExtensionUtils;
+
+const findModuleByPath = function (path) {
+ for (let module of pkcs11db.listModules()) {
+ if (module && module.libName === path) {
+ return module;
+ }
+ }
+ return null;
+};
+
+this.pkcs11 = class extends ExtensionAPI {
+ getAPI(context) {
+ let manifestCache = new DefaultMap(async name => {
+ let hostInfo = await NativeManifests.lookupManifest(
+ "pkcs11",
+ name,
+ context
+ );
+ if (hostInfo) {
+ // We don't normalize the absolute path below because
+ // `Path.normalize` throws when the target file doesn't
+ // exist, and that might be the case on non Windows
+ // builds.
+ let absolutePath = PathUtils.isAbsolute(hostInfo.manifest.path)
+ ? hostInfo.manifest.path
+ : PathUtils.joinRelative(
+ PathUtils.parent(hostInfo.path),
+ hostInfo.manifest.path
+ );
+
+ if (AppConstants.platform === "win") {
+ // On Windows, `hostInfo.manifest.path` is expected to be a normalized
+ // absolute path. On other platforms, this path may be relative but we
+ // cannot use `PathUtils.normalize()` on non-absolute paths.
+ absolutePath = PathUtils.normalize(absolutePath);
+ hostInfo.manifest.path = absolutePath;
+ }
+
+ // PathUtils.filename throws if the path is not an absolute path.
+ // The result is expected to be the basename of the file (without
+ // the dir path and the extension) so it is fine to use an absolute
+ // path that may not be normalized (non-Windows platforms).
+ let manifestLib = PathUtils.filename(absolutePath);
+
+ if (AppConstants.platform !== "linux") {
+ manifestLib = manifestLib.toLowerCase(manifestLib);
+ }
+ if (
+ manifestLib !== ctypes.libraryName("nssckbi") &&
+ manifestLib !== ctypes.libraryName("osclientcerts") &&
+ manifestLib !== ctypes.libraryName("ipcclientcerts")
+ ) {
+ return hostInfo.manifest;
+ }
+ }
+ return Promise.reject({ message: `No such PKCS#11 module ${name}` });
+ });
+ return {
+ pkcs11: {
+ /**
+ * Verify whether a given PKCS#11 module is installed.
+ *
+ * @param {string} name The name of the module, as specified in
+ * the manifest file.
+ * @returns {Promise} A Promise that resolves to true if the package
+ * is installed, or false if it is not. May be
+ * rejected if the module could not be found.
+ */
+ async isModuleInstalled(name) {
+ let manifest = await manifestCache.get(name);
+ return findModuleByPath(manifest.path) !== null;
+ },
+ /**
+ * Install a PKCS#11 module
+ *
+ * @param {string} name The name of the module, as specified in
+ * the manifest file.
+ * @param {integer} [flags = 0] Any flags to be passed on to the
+ * nsIPKCS11ModuleDB.addModule method
+ * @returns {Promise} When the Promise resolves, the module will have
+ * been installed. When it is rejected, the module
+ * either is already installed or could not be
+ * installed for some reason.
+ */
+ async installModule(name, flags = 0) {
+ let manifest = await manifestCache.get(name);
+ if (!manifest.description) {
+ return Promise.reject({
+ message: `The description field in the manifest for PKCS#11 module ${name} must have a value`,
+ });
+ }
+ pkcs11db.addModule(manifest.description, manifest.path, flags, 0);
+ },
+ /**
+ * Uninstall a PKCS#11 module
+ *
+ * @param {string} name The name of the module, as specified in
+ * the manifest file.
+ * @returns {Promise}. When the Promise resolves, the module will have
+ * been uninstalled. When it is rejected, the
+ * module either was not installed or could not be
+ * uninstalled for some reason.
+ */
+ async uninstallModule(name) {
+ let manifest = await manifestCache.get(name);
+ let module = findModuleByPath(manifest.path);
+ if (!module) {
+ return Promise.reject({
+ message: `The PKCS#11 module ${name} is not loaded`,
+ });
+ }
+ pkcs11db.deleteModule(module.name);
+ },
+ /**
+ * Get a list of slots for a given PKCS#11 module, with
+ * information on the token (if any) in the slot.
+ *
+ * The PKCS#11 standard defines slots as an abstract concept
+ * that may or may not have at most one token. In practice, when
+ * using PKCS#11 for smartcards (the most likely use case of
+ * PKCS#11 for Firefox), a slot corresponds to a cardreader, and
+ * a token corresponds to a card.
+ *
+ * @param {string} name The name of the PKCS#11 module, as
+ * specified in the manifest file.
+ * @returns {Promise} A promise that resolves to an array of objects
+ * with two properties. The `name` object contains
+ * the name of the slot; the `token` object is null
+ * if there is no token in the slot, or is an object
+ * describing various properties of the token if
+ * there is.
+ */
+ async getModuleSlots(name) {
+ let manifest = await manifestCache.get(name);
+ let module = findModuleByPath(manifest.path);
+ if (!module) {
+ return Promise.reject({
+ message: `The module ${name} is not installed`,
+ });
+ }
+ let rv = [];
+ for (let slot of module.listSlots()) {
+ let token = slot.getToken();
+ let slotobj = {
+ name: slot.name,
+ token: null,
+ };
+ if (slot.status != 1 /* SLOT_NOT_PRESENT */) {
+ slotobj.token = {
+ name: token.tokenName,
+ manufacturer: token.tokenManID,
+ HWVersion: token.tokenHWVersion,
+ FWVersion: token.tokenFWVersion,
+ serial: token.tokenSerialNumber,
+ isLoggedIn: token.isLoggedIn(),
+ };
+ }
+ rv.push(slotobj);
+ }
+ return rv;
+ },
+ },
+ };
+ }
+};