summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/parent/ext-pkcs11.js
blob: 696133bfc51c0ded75ae8fa153fa971eb6443a5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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;
        },
      },
    };
  }
};