summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/pk11wrap/pk11util.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/nss/lib/pk11wrap/pk11util.c1746
1 files changed, 1746 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/pk11util.c b/security/nss/lib/pk11wrap/pk11util.c
new file mode 100644
index 0000000000..2584ec3e8e
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11util.c
@@ -0,0 +1,1746 @@
+/* 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/. */
+/*
+ * Initialize the PCKS 11 subsystem
+ */
+#include "seccomon.h"
+#include "secmod.h"
+#include "nssilock.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pk11func.h"
+#include "pki3hack.h"
+#include "secerr.h"
+#include "dev.h"
+#include "dev3hack.h"
+#include "utilpars.h"
+#include "pkcs11uri.h"
+
+/* these are for displaying error messages */
+
+static SECMODModuleList *modules = NULL;
+static SECMODModuleList *modulesDB = NULL;
+static SECMODModuleList *modulesUnload = NULL;
+static SECMODModule *internalModule = NULL;
+static SECMODModule *defaultDBModule = NULL;
+static SECMODModule *pendingModule = NULL;
+static SECMODListLock *moduleLock = NULL;
+
+int secmod_PrivateModuleCount = 0;
+
+extern const PK11DefaultArrayEntry PK11_DefaultArray[];
+extern const int num_pk11_default_mechanisms;
+
+void
+SECMOD_Init()
+{
+ /* don't initialize twice */
+ if (moduleLock)
+ return;
+
+ moduleLock = SECMOD_NewListLock();
+ PK11_InitSlotLists();
+}
+
+SECStatus
+SECMOD_Shutdown()
+{
+ /* destroy the lock */
+ if (moduleLock) {
+ SECMOD_DestroyListLock(moduleLock);
+ moduleLock = NULL;
+ }
+ /* free the internal module */
+ if (internalModule) {
+ SECMOD_DestroyModule(internalModule);
+ internalModule = NULL;
+ }
+
+ /* free the default database module */
+ if (defaultDBModule) {
+ SECMOD_DestroyModule(defaultDBModule);
+ defaultDBModule = NULL;
+ }
+
+ /* destroy the list */
+ if (modules) {
+ SECMOD_DestroyModuleList(modules);
+ modules = NULL;
+ }
+
+ if (modulesDB) {
+ SECMOD_DestroyModuleList(modulesDB);
+ modulesDB = NULL;
+ }
+
+ if (modulesUnload) {
+ SECMOD_DestroyModuleList(modulesUnload);
+ modulesUnload = NULL;
+ }
+
+ /* make all the slots and the lists go away */
+ PK11_DestroySlotLists();
+
+ nss_DumpModuleLog();
+
+#ifdef DEBUG
+ if (PR_GetEnvSecure("NSS_STRICT_SHUTDOWN")) {
+ PORT_Assert(secmod_PrivateModuleCount == 0);
+ }
+#endif
+ if (secmod_PrivateModuleCount) {
+ PORT_SetError(SEC_ERROR_BUSY);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+PRBool
+SECMOD_GetSystemFIPSEnabled(void)
+{
+#ifdef LINUX
+#ifndef NSS_FIPS_DISABLED
+ FILE *f;
+ char d;
+ size_t size;
+
+ f = fopen("/proc/sys/crypto/fips_enabled", "r");
+ if (!f) {
+ return PR_FALSE;
+ }
+
+ size = fread(&d, 1, sizeof(d), f);
+ fclose(f);
+ if (size != sizeof(d)) {
+ return PR_FALSE;
+ }
+ if (d == '1') {
+ return PR_TRUE;
+ }
+#endif
+#endif
+ return PR_FALSE;
+}
+
+/*
+ * retrieve the internal module
+ */
+SECMODModule *
+SECMOD_GetInternalModule(void)
+{
+ return internalModule;
+}
+
+SECStatus
+secmod_AddModuleToList(SECMODModuleList **moduleList, SECMODModule *newModule)
+{
+ SECMODModuleList *mlp, *newListElement, *last = NULL;
+
+ newListElement = SECMOD_NewModuleListElement();
+ if (newListElement == NULL) {
+ return SECFailure;
+ }
+
+ newListElement->module = SECMOD_ReferenceModule(newModule);
+
+ SECMOD_GetWriteLock(moduleLock);
+ /* Added it to the end (This is very inefficient, but Adding a module
+ * on the fly should happen maybe 2-3 times through the life this program
+ * on a given computer, and this list should be *SHORT*. */
+ for (mlp = *moduleList; mlp != NULL; mlp = mlp->next) {
+ last = mlp;
+ }
+
+ if (last == NULL) {
+ *moduleList = newListElement;
+ } else {
+ SECMOD_AddList(last, newListElement, NULL);
+ }
+ SECMOD_ReleaseWriteLock(moduleLock);
+ return SECSuccess;
+}
+
+SECStatus
+SECMOD_AddModuleToList(SECMODModule *newModule)
+{
+ if (newModule->internal && !internalModule) {
+ internalModule = SECMOD_ReferenceModule(newModule);
+ }
+ return secmod_AddModuleToList(&modules, newModule);
+}
+
+SECStatus
+SECMOD_AddModuleToDBOnlyList(SECMODModule *newModule)
+{
+ if (defaultDBModule && SECMOD_GetDefaultModDBFlag(newModule)) {
+ SECMOD_DestroyModule(defaultDBModule);
+ defaultDBModule = SECMOD_ReferenceModule(newModule);
+ } else if (defaultDBModule == NULL) {
+ defaultDBModule = SECMOD_ReferenceModule(newModule);
+ }
+ return secmod_AddModuleToList(&modulesDB, newModule);
+}
+
+SECStatus
+SECMOD_AddModuleToUnloadList(SECMODModule *newModule)
+{
+ return secmod_AddModuleToList(&modulesUnload, newModule);
+}
+
+/*
+ * get the list of PKCS11 modules that are available.
+ */
+SECMODModuleList *
+SECMOD_GetDefaultModuleList()
+{
+ return modules;
+}
+SECMODModuleList *
+SECMOD_GetDeadModuleList()
+{
+ return modulesUnload;
+}
+SECMODModuleList *
+SECMOD_GetDBModuleList()
+{
+ return modulesDB;
+}
+
+/*
+ * This lock protects the global module lists.
+ * it also protects changes to the slot array (module->slots[]) and slot count
+ * (module->slotCount) in each module. It is a read/write lock with multiple
+ * readers or one writer. Writes are uncommon.
+ * Because of legacy considerations protection of the slot array and count is
+ * only necessary in applications if the application calls
+ * SECMOD_UpdateSlotList() or SECMOD_WaitForAnyTokenEvent(), though all new
+ * applications are encouraged to acquire this lock when reading the
+ * slot array information directly.
+ */
+SECMODListLock *
+SECMOD_GetDefaultModuleListLock()
+{
+ return moduleLock;
+}
+
+/*
+ * find a module by name, and add a reference to it.
+ * return that module.
+ */
+SECMODModule *
+SECMOD_FindModule(const char *name)
+{
+ SECMODModuleList *mlp;
+ SECMODModule *module = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return module;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ if (PORT_Strcmp(name, mlp->module->commonName) == 0) {
+ module = mlp->module;
+ SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+ if (module) {
+ goto found;
+ }
+ for (mlp = modulesUnload; mlp != NULL; mlp = mlp->next) {
+ if (PORT_Strcmp(name, mlp->module->commonName) == 0) {
+ module = mlp->module;
+ SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+
+found:
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ return module;
+}
+
+/*
+ * find a module by ID, and add a reference to it.
+ * return that module.
+ */
+SECMODModule *
+SECMOD_FindModuleByID(SECMODModuleID id)
+{
+ SECMODModuleList *mlp;
+ SECMODModule *module = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return module;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ if (id == mlp->module->moduleID) {
+ module = mlp->module;
+ SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ if (module == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ }
+ return module;
+}
+
+/*
+ * find the function pointer.
+ */
+SECMODModule *
+secmod_FindModuleByFuncPtr(void *funcPtr)
+{
+ SECMODModuleList *mlp;
+ SECMODModule *module = NULL;
+
+ SECMOD_GetReadLock(moduleLock);
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ /* paranoia, shouldn't ever happen */
+ if (!mlp->module) {
+ continue;
+ }
+ if (funcPtr == mlp->module->functionList) {
+ module = mlp->module;
+ SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ if (module == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ }
+ return module;
+}
+
+/*
+ * Find the Slot based on ID and the module.
+ */
+PK11SlotInfo *
+SECMOD_FindSlotByID(SECMODModule *module, CK_SLOT_ID slotID)
+{
+ int i;
+ PK11SlotInfo *slot = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return slot;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ for (i = 0; i < module->slotCount; i++) {
+ PK11SlotInfo *cSlot = module->slots[i];
+
+ if (cSlot->slotID == slotID) {
+ slot = PK11_ReferenceSlot(cSlot);
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED);
+ }
+ return slot;
+}
+
+/*
+ * lookup the Slot module based on it's module ID and slot ID.
+ */
+PK11SlotInfo *
+SECMOD_LookupSlot(SECMODModuleID moduleID, CK_SLOT_ID slotID)
+{
+ SECMODModule *module;
+ PK11SlotInfo *slot;
+
+ module = SECMOD_FindModuleByID(moduleID);
+ if (module == NULL)
+ return NULL;
+
+ slot = SECMOD_FindSlotByID(module, slotID);
+ SECMOD_DestroyModule(module);
+ return slot;
+}
+
+/*
+ * find a module by name or module pointer and delete it off the module list.
+ * optionally remove it from secmod.db.
+ */
+SECStatus
+SECMOD_DeleteModuleEx(const char *name, SECMODModule *mod,
+ int *type, PRBool permdb)
+{
+ SECMODModuleList *mlp;
+ SECMODModuleList **mlpp;
+ SECStatus rv = SECFailure;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return rv;
+ }
+
+ *type = SECMOD_EXTERNAL;
+
+ SECMOD_GetWriteLock(moduleLock);
+ for (mlpp = &modules, mlp = modules;
+ mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) {
+ if ((name && (PORT_Strcmp(name, mlp->module->commonName) == 0)) ||
+ mod == mlp->module) {
+ /* don't delete the internal module */
+ if (!mlp->module->internal) {
+ SECMOD_RemoveList(mlpp, mlp);
+ /* delete it after we release the lock */
+ rv = STAN_RemoveModuleFromDefaultTrustDomain(mlp->module);
+ } else if (mlp->module->isFIPS) {
+ *type = SECMOD_FIPS;
+ } else {
+ *type = SECMOD_INTERNAL;
+ }
+ break;
+ }
+ }
+ if (mlp) {
+ goto found;
+ }
+ /* not on the internal list, check the unload list */
+ for (mlpp = &modulesUnload, mlp = modulesUnload;
+ mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) {
+ if ((name && (PORT_Strcmp(name, mlp->module->commonName) == 0)) ||
+ mod == mlp->module) {
+ /* don't delete the internal module */
+ if (!mlp->module->internal) {
+ SECMOD_RemoveList(mlpp, mlp);
+ rv = SECSuccess;
+ } else if (mlp->module->isFIPS) {
+ *type = SECMOD_FIPS;
+ } else {
+ *type = SECMOD_INTERNAL;
+ }
+ break;
+ }
+ }
+found:
+ SECMOD_ReleaseWriteLock(moduleLock);
+
+ if (rv == SECSuccess) {
+ if (permdb) {
+ SECMOD_DeletePermDB(mlp->module);
+ }
+ SECMOD_DestroyModuleListElement(mlp);
+ }
+ return rv;
+}
+
+/*
+ * find a module by name and delete it off the module list
+ */
+SECStatus
+SECMOD_DeleteModule(const char *name, int *type)
+{
+ return SECMOD_DeleteModuleEx(name, NULL, type, PR_TRUE);
+}
+
+/*
+ * find a module by name and delete it off the module list
+ */
+SECStatus
+SECMOD_DeleteInternalModule(const char *name)
+{
+#ifndef NSS_FIPS_DISABLED
+ SECMODModuleList *mlp;
+ SECMODModuleList **mlpp;
+#endif
+ SECStatus rv = SECFailure;
+
+ if (SECMOD_GetSystemFIPSEnabled() || pendingModule) {
+ PORT_SetError(SEC_ERROR_MODULE_STUCK);
+ return rv;
+ }
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return rv;
+ }
+
+#ifdef NSS_FIPS_DISABLED
+ PORT_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR);
+ return rv;
+#else
+ SECMOD_GetWriteLock(moduleLock);
+ for (mlpp = &modules, mlp = modules;
+ mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) {
+ if (PORT_Strcmp(name, mlp->module->commonName) == 0) {
+ /* don't delete the internal module */
+ if (mlp->module->internal) {
+ SECMOD_RemoveList(mlpp, mlp);
+ rv = STAN_RemoveModuleFromDefaultTrustDomain(mlp->module);
+ }
+ break;
+ }
+ }
+ SECMOD_ReleaseWriteLock(moduleLock);
+
+ if (rv == SECSuccess) {
+ SECMODModule *newModule, *oldModule;
+
+ if (mlp->module->isFIPS) {
+ newModule = SECMOD_CreateModule(NULL, SECMOD_INT_NAME,
+ NULL, SECMOD_INT_FLAGS);
+ } else {
+ newModule = SECMOD_CreateModule(NULL, SECMOD_FIPS_NAME,
+ NULL, SECMOD_FIPS_FLAGS);
+ }
+ if (newModule) {
+ PK11SlotInfo *slot;
+ newModule->libraryParams =
+ PORT_ArenaStrdup(newModule->arena, mlp->module->libraryParams);
+ /* if an explicit internal key slot has been set, reset it */
+ slot = pk11_SwapInternalKeySlot(NULL);
+ if (slot) {
+ secmod_SetInternalKeySlotFlag(newModule, PR_TRUE);
+ }
+ rv = SECMOD_AddModule(newModule);
+ if (rv != SECSuccess) {
+ /* load failed, restore the internal key slot */
+ pk11_SetInternalKeySlot(slot);
+ SECMOD_DestroyModule(newModule);
+ newModule = NULL;
+ }
+ /* free the old explicit internal key slot, we now have a new one */
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+ }
+ if (newModule == NULL) {
+ SECMODModuleList *last = NULL, *mlp2;
+ /* we're in pretty deep trouble if this happens...Security
+ * not going to work well... try to put the old module back on
+ * the list */
+ SECMOD_GetWriteLock(moduleLock);
+ for (mlp2 = modules; mlp2 != NULL; mlp2 = mlp->next) {
+ last = mlp2;
+ }
+
+ if (last == NULL) {
+ modules = mlp;
+ } else {
+ SECMOD_AddList(last, mlp, NULL);
+ }
+ SECMOD_ReleaseWriteLock(moduleLock);
+ return SECFailure;
+ }
+ pendingModule = oldModule = internalModule;
+ internalModule = NULL;
+ SECMOD_DestroyModule(oldModule);
+ SECMOD_DeletePermDB(mlp->module);
+ SECMOD_DestroyModuleListElement(mlp);
+ internalModule = newModule; /* adopt the module */
+ }
+ return rv;
+#endif
+}
+
+SECStatus
+SECMOD_AddModule(SECMODModule *newModule)
+{
+ SECStatus rv;
+ SECMODModule *oldModule;
+
+ /* Test if a module w/ the same name already exists */
+ /* and return SECWouldBlock if so. */
+ /* We should probably add a new return value such as */
+ /* SECDublicateModule, but to minimize ripples, I'll */
+ /* give SECWouldBlock a new meaning */
+ if ((oldModule = SECMOD_FindModule(newModule->commonName)) != NULL) {
+ SECMOD_DestroyModule(oldModule);
+ return SECWouldBlock;
+ /* module already exists. */
+ }
+
+ rv = secmod_LoadPKCS11Module(newModule, NULL);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ if (newModule->parent == NULL) {
+ newModule->parent = SECMOD_ReferenceModule(defaultDBModule);
+ }
+
+ SECMOD_AddPermDB(newModule);
+ SECMOD_AddModuleToList(newModule);
+
+ rv = STAN_AddModuleToDefaultTrustDomain(newModule);
+
+ return rv;
+}
+
+PK11SlotInfo *
+SECMOD_FindSlot(SECMODModule *module, const char *name)
+{
+ int i;
+ char *string;
+ PK11SlotInfo *retSlot = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return retSlot;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ for (i = 0; i < module->slotCount; i++) {
+ PK11SlotInfo *slot = module->slots[i];
+
+ if (PK11_IsPresent(slot)) {
+ string = PK11_GetTokenName(slot);
+ } else {
+ string = PK11_GetSlotName(slot);
+ }
+ if (PORT_Strcmp(name, string) == 0) {
+ retSlot = PK11_ReferenceSlot(slot);
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ if (retSlot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED);
+ }
+ return retSlot;
+}
+
+SECStatus
+PK11_GetModInfo(SECMODModule *mod, CK_INFO *info)
+{
+ CK_RV crv;
+
+ if (mod->functionList == NULL)
+ return SECFailure;
+ crv = PK11_GETTAB(mod)->C_GetInfo(info);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ }
+ return (crv == CKR_OK) ? SECSuccess : SECFailure;
+}
+
+char *
+PK11_GetModuleURI(SECMODModule *mod)
+{
+ CK_INFO info;
+ PK11URI *uri;
+ char *ret = NULL;
+ PK11URIAttribute attrs[3];
+ size_t nattrs = 0;
+ char libraryManufacturer[32 + 1], libraryDescription[32 + 1], libraryVersion[8];
+
+ if (PK11_GetModInfo(mod, &info) == SECFailure) {
+ return NULL;
+ }
+
+ PK11_MakeString(NULL, libraryManufacturer, (char *)info.manufacturerID,
+ sizeof(info.manufacturerID));
+ if (*libraryManufacturer != '\0') {
+ attrs[nattrs].name = PK11URI_PATTR_LIBRARY_MANUFACTURER;
+ attrs[nattrs].value = libraryManufacturer;
+ nattrs++;
+ }
+
+ PK11_MakeString(NULL, libraryDescription, (char *)info.libraryDescription,
+ sizeof(info.libraryDescription));
+ if (*libraryDescription != '\0') {
+ attrs[nattrs].name = PK11URI_PATTR_LIBRARY_DESCRIPTION;
+ attrs[nattrs].value = libraryDescription;
+ nattrs++;
+ }
+
+ PR_snprintf(libraryVersion, sizeof(libraryVersion), "%d.%d",
+ info.libraryVersion.major, info.libraryVersion.minor);
+ attrs[nattrs].name = PK11URI_PATTR_LIBRARY_VERSION;
+ attrs[nattrs].value = libraryVersion;
+ nattrs++;
+
+ uri = PK11URI_CreateURI(attrs, nattrs, NULL, 0);
+ if (uri == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ ret = PK11URI_FormatURI(NULL, uri);
+ PK11URI_DestroyURI(uri);
+ if (ret == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ return ret;
+}
+
+/* Determine if we have the FIP's module loaded as the default
+ * module to trigger other bogus FIPS requirements in PKCS #12 and
+ * SSL
+ */
+PRBool
+PK11_IsFIPS(void)
+{
+ SECMODModule *mod = SECMOD_GetInternalModule();
+
+ if (mod && mod->internal) {
+ return mod->isFIPS;
+ }
+
+ return PR_FALSE;
+}
+
+/* combines NewModule() & AddModule */
+/* give a string for the module name & the full-path for the dll, */
+/* installs the PKCS11 module & update registry */
+SECStatus
+SECMOD_AddNewModuleEx(const char *moduleName, const char *dllPath,
+ unsigned long defaultMechanismFlags,
+ unsigned long cipherEnableFlags,
+ char *modparms, char *nssparms)
+{
+ SECMODModule *module;
+ SECStatus result = SECFailure;
+ int s, i;
+ PK11SlotInfo *slot;
+
+ PR_SetErrorText(0, NULL);
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return result;
+ }
+
+ module = SECMOD_CreateModule(dllPath, moduleName, modparms, nssparms);
+
+ if (module == NULL) {
+ return result;
+ }
+
+ if (module->dllName != NULL) {
+ if (module->dllName[0] != 0) {
+ result = SECMOD_AddModule(module);
+ if (result == SECSuccess) {
+ /* turn on SSL cipher enable flags */
+ module->ssl[0] = cipherEnableFlags;
+
+ SECMOD_GetReadLock(moduleLock);
+ /* check each slot to turn on appropriate mechanisms */
+ for (s = 0; s < module->slotCount; s++) {
+ slot = (module->slots)[s];
+ /* for each possible mechanism */
+ for (i = 0; i < num_pk11_default_mechanisms; i++) {
+ /* we are told to turn it on by default ? */
+ PRBool add =
+ (PK11_DefaultArray[i].flag & defaultMechanismFlags) ? PR_TRUE : PR_FALSE;
+ result = PK11_UpdateSlotAttribute(slot,
+ &(PK11_DefaultArray[i]), add);
+ if (result != SECSuccess) {
+ SECMOD_ReleaseReadLock(moduleLock);
+ SECMOD_DestroyModule(module);
+ return result;
+ }
+ } /* for each mechanism */
+ /* disable each slot if the defaultFlags say so */
+ if (defaultMechanismFlags & PK11_DISABLE_FLAG) {
+ PK11_UserDisableSlot(slot);
+ }
+ } /* for each slot of this module */
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ /* delete and re-add module in order to save changes
+ * to the module */
+ result = SECMOD_UpdateModule(module);
+ }
+ }
+ }
+ SECMOD_DestroyModule(module);
+ return result;
+}
+
+SECStatus
+SECMOD_AddNewModule(const char *moduleName, const char *dllPath,
+ unsigned long defaultMechanismFlags,
+ unsigned long cipherEnableFlags)
+{
+ return SECMOD_AddNewModuleEx(moduleName, dllPath, defaultMechanismFlags,
+ cipherEnableFlags,
+ NULL, NULL); /* don't pass module or nss params */
+}
+
+SECStatus
+SECMOD_UpdateModule(SECMODModule *module)
+{
+ SECStatus result;
+
+ result = SECMOD_DeletePermDB(module);
+
+ if (result == SECSuccess) {
+ result = SECMOD_AddPermDB(module);
+ }
+ return result;
+}
+
+/* Public & Internal(Security Library) representation of
+ * encryption mechanism flags conversion */
+
+/* Currently, the only difference is that internal representation
+ * puts RANDOM_FLAG at bit 31 (Most-significant bit), but
+ * public representation puts this bit at bit 28
+ */
+unsigned long
+SECMOD_PubMechFlagstoInternal(unsigned long publicFlags)
+{
+ unsigned long internalFlags = publicFlags;
+
+ if (publicFlags & PUBLIC_MECH_RANDOM_FLAG) {
+ internalFlags &= ~PUBLIC_MECH_RANDOM_FLAG;
+ internalFlags |= SECMOD_RANDOM_FLAG;
+ }
+ return internalFlags;
+}
+
+unsigned long
+SECMOD_InternaltoPubMechFlags(unsigned long internalFlags)
+{
+ unsigned long publicFlags = internalFlags;
+
+ if (internalFlags & SECMOD_RANDOM_FLAG) {
+ publicFlags &= ~SECMOD_RANDOM_FLAG;
+ publicFlags |= PUBLIC_MECH_RANDOM_FLAG;
+ }
+ return publicFlags;
+}
+
+/* Public & Internal(Security Library) representation of */
+/* cipher flags conversion */
+/* Note: currently they are just stubs */
+unsigned long
+SECMOD_PubCipherFlagstoInternal(unsigned long publicFlags)
+{
+ return publicFlags;
+}
+
+unsigned long
+SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags)
+{
+ return internalFlags;
+}
+
+/* Funtion reports true if module of modType is installed/configured */
+PRBool
+SECMOD_IsModulePresent(unsigned long int pubCipherEnableFlags)
+{
+ PRBool result = PR_FALSE;
+ SECMODModuleList *mods;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return result;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ mods = SECMOD_GetDefaultModuleList();
+ for (; mods != NULL; mods = mods->next) {
+ if (mods->module->ssl[0] &
+ SECMOD_PubCipherFlagstoInternal(pubCipherEnableFlags)) {
+ result = PR_TRUE;
+ }
+ }
+
+ SECMOD_ReleaseReadLock(moduleLock);
+ return result;
+}
+
+/* create a new ModuleListElement */
+SECMODModuleList *
+SECMOD_NewModuleListElement(void)
+{
+ SECMODModuleList *newModList;
+
+ newModList = (SECMODModuleList *)PORT_Alloc(sizeof(SECMODModuleList));
+ if (newModList) {
+ newModList->next = NULL;
+ newModList->module = NULL;
+ }
+ return newModList;
+}
+
+/*
+ * make a new reference to a module so It doesn't go away on us
+ */
+SECMODModule *
+SECMOD_ReferenceModule(SECMODModule *module)
+{
+ PZ_Lock(module->refLock);
+ PORT_Assert(module->refCount > 0);
+
+ module->refCount++;
+ PZ_Unlock(module->refLock);
+ return module;
+}
+
+/* destroy an existing module */
+void
+SECMOD_DestroyModule(SECMODModule *module)
+{
+ PRBool willfree = PR_FALSE;
+ int slotCount;
+ int i;
+
+ PZ_Lock(module->refLock);
+ if (module->refCount-- == 1) {
+ willfree = PR_TRUE;
+ }
+ PORT_Assert(willfree || (module->refCount > 0));
+ PZ_Unlock(module->refLock);
+
+ if (!willfree) {
+ return;
+ }
+
+ if (module->parent != NULL) {
+ SECMODModule *parent = module->parent;
+ /* paranoia, don't loop forever if the modules are looped */
+ module->parent = NULL;
+ SECMOD_DestroyModule(parent);
+ }
+
+ /* slots can't really disappear until our module starts freeing them,
+ * so this check is safe */
+ slotCount = module->slotCount;
+ if (slotCount == 0) {
+ SECMOD_SlotDestroyModule(module, PR_FALSE);
+ return;
+ }
+
+ /* now free all out slots, when they are done, they will cause the
+ * module to disappear altogether */
+ for (i = 0; i < slotCount; i++) {
+ if (!module->slots[i]->disabled) {
+ PK11_ClearSlotList(module->slots[i]);
+ }
+ PK11_FreeSlot(module->slots[i]);
+ }
+ /* WARNING: once the last slot has been freed is it possible (even likely)
+ * that module is no more... touching it now is a good way to go south */
+}
+
+/* we can only get here if we've destroyed the module, or some one has
+ * erroneously freed a slot that wasn't referenced. */
+void
+SECMOD_SlotDestroyModule(SECMODModule *module, PRBool fromSlot)
+{
+ PRBool willfree = PR_FALSE;
+ if (fromSlot) {
+ PORT_Assert(module->refCount == 0);
+ PZ_Lock(module->refLock);
+ if (module->slotCount-- == 1) {
+ willfree = PR_TRUE;
+ }
+ PORT_Assert(willfree || (module->slotCount > 0));
+ PZ_Unlock(module->refLock);
+ if (!willfree)
+ return;
+ }
+
+ if (module == pendingModule) {
+ pendingModule = NULL;
+ }
+
+ if (module->loaded) {
+ SECMOD_UnloadModule(module);
+ }
+ PZ_DestroyLock(module->refLock);
+ PORT_FreeArena(module->arena, PR_FALSE);
+ secmod_PrivateModuleCount--;
+}
+
+/* destroy a list element
+ * this destroys a single element, and returns the next element
+ * on the chain. It makes it easy to implement for loops to delete
+ * the chain. It also make deleting a single element easy */
+SECMODModuleList *
+SECMOD_DestroyModuleListElement(SECMODModuleList *element)
+{
+ SECMODModuleList *next = element->next;
+
+ if (element->module) {
+ SECMOD_DestroyModule(element->module);
+ element->module = NULL;
+ }
+ PORT_Free(element);
+ return next;
+}
+
+/*
+ * Destroy an entire module list
+ */
+void
+SECMOD_DestroyModuleList(SECMODModuleList *list)
+{
+ SECMODModuleList *lp;
+
+ for (lp = list; lp != NULL; lp = SECMOD_DestroyModuleListElement(lp))
+ ;
+}
+
+PRBool
+SECMOD_CanDeleteInternalModule(void)
+{
+#ifdef NSS_FIPS_DISABLED
+ return PR_FALSE;
+#else
+ return (PRBool)((pendingModule == NULL) && !SECMOD_GetSystemFIPSEnabled());
+#endif
+}
+
+/*
+ * check to see if the module has added new slots. PKCS 11 v2.20 allows for
+ * modules to add new slots, but never remove them. Slots cannot be added
+ * between a call to C_GetSlotLlist(Flag, NULL, &count) and the subsequent
+ * C_GetSlotList(flag, &data, &count) so that the array doesn't accidently
+ * grow on the caller. It is permissible for the slots to increase between
+ * successive calls with NULL to get the size.
+ *
+ * Caller must not hold a module list read lock.
+ */
+SECStatus
+SECMOD_UpdateSlotList(SECMODModule *mod)
+{
+ CK_RV crv;
+ CK_ULONG count;
+ CK_ULONG i, oldCount;
+ PRBool freeRef = PR_FALSE;
+ void *mark = NULL;
+ CK_ULONG *slotIDs = NULL;
+ PK11SlotInfo **newSlots = NULL;
+ PK11SlotInfo **oldSlots = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+
+ /* C_GetSlotList is not a session function, make sure
+ * calls are serialized */
+ PZ_Lock(mod->refLock);
+ freeRef = PR_TRUE;
+ /* see if the number of slots have changed */
+ crv = PK11_GETTAB(mod)->C_GetSlotList(PR_FALSE, NULL, &count);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+ /* nothing new, blow out early, we want this function to be quick
+ * and cheap in the normal case */
+ if (count == mod->slotCount) {
+ PZ_Unlock(mod->refLock);
+ return SECSuccess;
+ }
+ if (count < (CK_ULONG)mod->slotCount) {
+ /* shouldn't happen with a properly functioning PKCS #11 module */
+ PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
+ goto loser;
+ }
+
+ /* get the new slot list */
+ slotIDs = PORT_NewArray(CK_SLOT_ID, count);
+ if (slotIDs == NULL) {
+ goto loser;
+ }
+
+ crv = PK11_GETTAB(mod)->C_GetSlotList(PR_FALSE, slotIDs, &count);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+ freeRef = PR_FALSE;
+ PZ_Unlock(mod->refLock);
+ mark = PORT_ArenaMark(mod->arena);
+ if (mark == NULL) {
+ goto loser;
+ }
+ newSlots = PORT_ArenaZNewArray(mod->arena, PK11SlotInfo *, count);
+
+ /* walk down the new slot ID list returned from the module. We keep
+ * the old slots which match a returned ID, and we initialize the new
+ * slots. */
+ for (i = 0; i < count; i++) {
+ PK11SlotInfo *slot = SECMOD_FindSlotByID(mod, slotIDs[i]);
+
+ if (!slot) {
+ /* we have a new slot create a new slot data structure */
+ slot = PK11_NewSlotInfo(mod);
+ if (!slot) {
+ goto loser;
+ }
+ PK11_InitSlot(mod, slotIDs[i], slot);
+ STAN_InitTokenForSlotInfo(NULL, slot);
+ }
+ newSlots[i] = slot;
+ }
+ STAN_ResetTokenInterator(NULL);
+ PORT_Free(slotIDs);
+ slotIDs = NULL;
+ PORT_ArenaUnmark(mod->arena, mark);
+
+ /* until this point we're still using the old slot list. Now we update
+ * module slot list. We update the slots (array) first then the count,
+ * since we've already guarrenteed that count has increased (just in case
+ * someone is looking at the slots field of module without holding the
+ * moduleLock */
+ SECMOD_GetWriteLock(moduleLock);
+ oldCount = mod->slotCount;
+ oldSlots = mod->slots;
+ mod->slots = newSlots; /* typical arena 'leak'... old mod->slots is
+ * allocated out of the module arena and won't
+ * be freed until the module is freed */
+ mod->slotCount = count;
+ SECMOD_ReleaseWriteLock(moduleLock);
+ /* free our old references before forgetting about oldSlot*/
+ for (i = 0; i < oldCount; i++) {
+ PK11_FreeSlot(oldSlots[i]);
+ }
+ return SECSuccess;
+
+loser:
+ if (freeRef) {
+ PZ_Unlock(mod->refLock);
+ }
+ if (slotIDs) {
+ PORT_Free(slotIDs);
+ }
+ /* free all the slots we allocated. newSlots are part of the
+ * mod arena. NOTE: the newSlots array contain both new and old
+ * slots, but we kept a reference to the old slots when we built the new
+ * array, so we need to free all the slots in newSlots array. */
+ if (newSlots) {
+ for (i = 0; i < count; i++) {
+ if (newSlots[i] == NULL) {
+ break; /* hit the last one */
+ }
+ PK11_FreeSlot(newSlots[i]);
+ }
+ }
+ /* must come after freeing newSlots */
+ if (mark) {
+ PORT_ArenaRelease(mod->arena, mark);
+ }
+ return SECFailure;
+}
+
+/*
+ * this handles modules that do not support C_WaitForSlotEvent().
+ * The internal flags are stored. Note that C_WaitForSlotEvent() does not
+ * have a timeout, so we don't have one for handleWaitForSlotEvent() either.
+ */
+PK11SlotInfo *
+secmod_HandleWaitForSlotEvent(SECMODModule *mod, unsigned long flags,
+ PRIntervalTime latency)
+{
+ PRBool removableSlotsFound = PR_FALSE;
+ int i;
+ int error = SEC_ERROR_NO_EVENT;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return NULL;
+ }
+ PZ_Lock(mod->refLock);
+ if (mod->evControlMask & SECMOD_END_WAIT) {
+ mod->evControlMask &= ~SECMOD_END_WAIT;
+ PZ_Unlock(mod->refLock);
+ PORT_SetError(SEC_ERROR_NO_EVENT);
+ return NULL;
+ }
+ mod->evControlMask |= SECMOD_WAIT_SIMULATED_EVENT;
+ while (mod->evControlMask & SECMOD_WAIT_SIMULATED_EVENT) {
+ PZ_Unlock(mod->refLock);
+ /* now is a good time to see if new slots have been added */
+ SECMOD_UpdateSlotList(mod);
+
+ /* loop through all the slots on a module */
+ SECMOD_GetReadLock(moduleLock);
+ for (i = 0; i < mod->slotCount; i++) {
+ PK11SlotInfo *slot = mod->slots[i];
+ PRUint16 series;
+ PRBool present;
+
+ /* perm modules do not change */
+ if (slot->isPerm) {
+ continue;
+ }
+ removableSlotsFound = PR_TRUE;
+ /* simulate the PKCS #11 module flags. are the flags different
+ * from the last time we called? */
+ series = slot->series;
+ present = PK11_IsPresent(slot);
+ if ((slot->flagSeries != series) || (slot->flagState != present)) {
+ slot->flagState = present;
+ slot->flagSeries = series;
+ SECMOD_ReleaseReadLock(moduleLock);
+ PZ_Lock(mod->refLock);
+ mod->evControlMask &= ~SECMOD_END_WAIT;
+ PZ_Unlock(mod->refLock);
+ return PK11_ReferenceSlot(slot);
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ /* if everything was perm modules, don't lock up forever */
+ if ((mod->slotCount != 0) && !removableSlotsFound) {
+ error = SEC_ERROR_NO_SLOT_SELECTED;
+ PZ_Lock(mod->refLock);
+ break;
+ }
+ if (flags & CKF_DONT_BLOCK) {
+ PZ_Lock(mod->refLock);
+ break;
+ }
+ PR_Sleep(latency);
+ PZ_Lock(mod->refLock);
+ }
+ mod->evControlMask &= ~SECMOD_END_WAIT;
+ PZ_Unlock(mod->refLock);
+ PORT_SetError(error);
+ return NULL;
+}
+
+/*
+ * this function waits for a token event on any slot of a given module
+ * This function should not be called from more than one thread of the
+ * same process (though other threads can make other library calls
+ * on this module while this call is blocked).
+ */
+PK11SlotInfo *
+SECMOD_WaitForAnyTokenEvent(SECMODModule *mod, unsigned long flags,
+ PRIntervalTime latency)
+{
+ CK_SLOT_ID id;
+ CK_RV crv;
+ PK11SlotInfo *slot;
+
+ if (!pk11_getFinalizeModulesOption() ||
+ ((mod->cryptokiVersion.major == 2) &&
+ (mod->cryptokiVersion.minor < 1))) {
+ /* if we are sharing the module with other software in our
+ * address space, we can't reliably use C_WaitForSlotEvent(),
+ * and if the module is version 2.0, C_WaitForSlotEvent() doesn't
+ * exist */
+ return secmod_HandleWaitForSlotEvent(mod, flags, latency);
+ }
+ /* first the the PKCS #11 call */
+ PZ_Lock(mod->refLock);
+ if (mod->evControlMask & SECMOD_END_WAIT) {
+ goto end_wait;
+ }
+ mod->evControlMask |= SECMOD_WAIT_PKCS11_EVENT;
+ PZ_Unlock(mod->refLock);
+ crv = PK11_GETTAB(mod)->C_WaitForSlotEvent(flags, &id, NULL);
+ PZ_Lock(mod->refLock);
+ mod->evControlMask &= ~SECMOD_WAIT_PKCS11_EVENT;
+ /* if we are in end wait, short circuit now, don't even risk
+ * going into secmod_HandleWaitForSlotEvent */
+ if (mod->evControlMask & SECMOD_END_WAIT) {
+ goto end_wait;
+ }
+ PZ_Unlock(mod->refLock);
+ if (crv == CKR_FUNCTION_NOT_SUPPORTED) {
+ /* module doesn't support that call, simulate it */
+ return secmod_HandleWaitForSlotEvent(mod, flags, latency);
+ }
+ if (crv != CKR_OK) {
+ /* we can get this error if finalize was called while we were
+ * still running. This is the only way to force a C_WaitForSlotEvent()
+ * to return in PKCS #11. In this case, just return that there
+ * was no event. */
+ if (crv == CKR_CRYPTOKI_NOT_INITIALIZED) {
+ PORT_SetError(SEC_ERROR_NO_EVENT);
+ } else {
+ PORT_SetError(PK11_MapError(crv));
+ }
+ return NULL;
+ }
+ slot = SECMOD_FindSlotByID(mod, id);
+ if (slot == NULL) {
+ /* possibly a new slot that was added? */
+ SECMOD_UpdateSlotList(mod);
+ slot = SECMOD_FindSlotByID(mod, id);
+ }
+ /* if we are in the delay period for the "isPresent" call, reset
+ * the delay since we know things have probably changed... */
+ if (slot) {
+ NSSToken *nssToken = PK11Slot_GetNSSToken(slot);
+ if (nssToken) {
+ if (nssToken->slot) {
+ nssSlot_ResetDelay(nssToken->slot);
+ }
+ (void)nssToken_Destroy(nssToken);
+ }
+ }
+ return slot;
+
+/* must be called with the lock on. */
+end_wait:
+ mod->evControlMask &= ~SECMOD_END_WAIT;
+ PZ_Unlock(mod->refLock);
+ PORT_SetError(SEC_ERROR_NO_EVENT);
+ return NULL;
+}
+
+/*
+ * This function "wakes up" WaitForAnyTokenEvent. It's a pretty drastic
+ * function, possibly bringing down the pkcs #11 module in question. This
+ * should be OK because 1) it does reinitialize, and 2) it should only be
+ * called when we are on our way to tear the whole system down anyway.
+ */
+SECStatus
+SECMOD_CancelWait(SECMODModule *mod)
+{
+ unsigned long controlMask;
+ SECStatus rv = SECSuccess;
+ CK_RV crv;
+
+ PZ_Lock(mod->refLock);
+ mod->evControlMask |= SECMOD_END_WAIT;
+ controlMask = mod->evControlMask;
+ if (controlMask & SECMOD_WAIT_PKCS11_EVENT) {
+ if (!pk11_getFinalizeModulesOption()) {
+ /* can't get here unless pk11_getFinalizeModulesOption is set */
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ goto loser;
+ }
+ /* NOTE: this call will drop all transient keys, in progress
+ * operations, and any authentication. This is the only documented
+ * way to get WaitForSlotEvent to return. Also note: for non-thread
+ * safe tokens, we need to hold the module lock, this is not yet at
+ * system shutdown/startup time, so we need to protect these calls */
+ crv = PK11_GETTAB(mod)->C_Finalize(NULL);
+ /* ok, we slammed the module down, now we need to reinit it in case
+ * we intend to use it again */
+ if (CKR_OK == crv) {
+ PRBool alreadyLoaded;
+ secmod_ModuleInit(mod, NULL, &alreadyLoaded);
+ } else {
+ /* Finalized failed for some reason, notify the application
+ * so maybe it has a prayer of recovering... */
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure;
+ }
+ } else if (controlMask & SECMOD_WAIT_SIMULATED_EVENT) {
+ mod->evControlMask &= ~SECMOD_WAIT_SIMULATED_EVENT;
+ /* Simulated events will eventually timeout
+ * and wake up in the loop */
+ }
+loser:
+ PZ_Unlock(mod->refLock);
+ return rv;
+}
+
+/*
+ * check to see if the module has removable slots that we may need to
+ * watch for.
+ */
+PRBool
+SECMOD_HasRemovableSlots(SECMODModule *mod)
+{
+ PRBool ret = PR_FALSE;
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return ret;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ ret = SECMOD_LockedModuleHasRemovableSlots(mod);
+ SECMOD_ReleaseReadLock(moduleLock);
+ return ret;
+}
+
+PRBool
+SECMOD_LockedModuleHasRemovableSlots(SECMODModule *mod)
+{
+ int i;
+ PRBool ret;
+ if (mod->slotCount == 0) {
+ return PR_TRUE;
+ }
+
+ ret = PR_FALSE;
+ for (i = 0; i < mod->slotCount; i++) {
+ PK11SlotInfo *slot = mod->slots[i];
+ /* perm modules are not inserted or removed */
+ if (slot->isPerm) {
+ continue;
+ }
+ ret = PR_TRUE;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * helper function to actually create and destroy user defined slots
+ */
+static SECStatus
+secmod_UserDBOp(PK11SlotInfo *slot, CK_OBJECT_CLASS objClass,
+ const char *sendSpec)
+{
+ CK_OBJECT_HANDLE dummy;
+ CK_ATTRIBUTE template[2];
+ CK_ATTRIBUTE *attrs = template;
+ CK_RV crv;
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_NSS_MODULE_SPEC, (unsigned char *)sendSpec,
+ strlen(sendSpec) + 1);
+ attrs++;
+
+ PORT_Assert(attrs - template <= 2);
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_CreateNewObject(slot, slot->session,
+ template, attrs - template, PR_FALSE, &dummy);
+ PK11_ExitSlotMonitor(slot);
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECMOD_UpdateSlotList(slot->module);
+}
+
+/*
+ * return true if the selected slot ID is not present or doesn't exist
+ */
+static PRBool
+secmod_SlotIsEmpty(SECMODModule *mod, CK_SLOT_ID slotID)
+{
+ PK11SlotInfo *slot = SECMOD_LookupSlot(mod->moduleID, slotID);
+ if (slot) {
+ PRBool present = PK11_IsPresent(slot);
+ PK11_FreeSlot(slot);
+ if (present) {
+ return PR_FALSE;
+ }
+ }
+ /* it doesn't exist or isn't present, it's available */
+ return PR_TRUE;
+}
+
+/*
+ * Find an unused slot id in module.
+ */
+static CK_SLOT_ID
+secmod_FindFreeSlot(SECMODModule *mod)
+{
+ CK_SLOT_ID i, minSlotID, maxSlotID;
+
+ /* look for a free slot id on the internal module */
+ if (mod->internal && mod->isFIPS) {
+ minSlotID = SFTK_MIN_FIPS_USER_SLOT_ID;
+ maxSlotID = SFTK_MAX_FIPS_USER_SLOT_ID;
+ } else {
+ minSlotID = SFTK_MIN_USER_SLOT_ID;
+ maxSlotID = SFTK_MAX_USER_SLOT_ID;
+ }
+ for (i = minSlotID; i < maxSlotID; i++) {
+ if (secmod_SlotIsEmpty(mod, i)) {
+ return i;
+ }
+ }
+ PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED);
+ return (CK_SLOT_ID)-1;
+}
+
+/*
+ * Attempt to open a new slot.
+ *
+ * This works the same os OpenUserDB except it can be called against
+ * any module that understands the softoken protocol for opening new
+ * slots, not just the softoken itself. If the selected module does not
+ * understand the protocol, C_CreateObject will fail with
+ * CKR_INVALID_ATTRIBUTE, and SECMOD_OpenNewSlot will return NULL and set
+ * SEC_ERROR_BAD_DATA.
+ *
+ * NewSlots can be closed with SECMOD_CloseUserDB();
+ *
+ * Modulespec is module dependent.
+ */
+PK11SlotInfo *
+SECMOD_OpenNewSlot(SECMODModule *mod, const char *moduleSpec)
+{
+ CK_SLOT_ID slotID = 0;
+ PK11SlotInfo *slot;
+ char *escSpec;
+ char *sendSpec;
+ SECStatus rv;
+
+ slotID = secmod_FindFreeSlot(mod);
+ if (slotID == (CK_SLOT_ID)-1) {
+ return NULL;
+ }
+
+ if (mod->slotCount == 0) {
+ return NULL;
+ }
+
+ /* just grab the first slot in the module, any present slot should work */
+ slot = PK11_ReferenceSlot(mod->slots[0]);
+ if (slot == NULL) {
+ return NULL;
+ }
+
+ /* we've found the slot, now build the moduleSpec */
+ escSpec = NSSUTIL_DoubleEscape(moduleSpec, '>', ']');
+ if (escSpec == NULL) {
+ PK11_FreeSlot(slot);
+ return NULL;
+ }
+ sendSpec = PR_smprintf("tokens=[0x%x=<%s>]", slotID, escSpec);
+ PORT_Free(escSpec);
+
+ if (sendSpec == NULL) {
+ /* PR_smprintf does not set SEC_ERROR_NO_MEMORY on failure. */
+ PK11_FreeSlot(slot);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+ rv = secmod_UserDBOp(slot, CKO_NSS_NEWSLOT, sendSpec);
+ PR_smprintf_free(sendSpec);
+ PK11_FreeSlot(slot);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+
+ slot = SECMOD_FindSlotByID(mod, slotID);
+ if (slot) {
+ /* if we are in the delay period for the "isPresent" call, reset
+ * the delay since we know things have probably changed... */
+ NSSToken *nssToken = PK11Slot_GetNSSToken(slot);
+ if (nssToken) {
+ if (nssToken->slot) {
+ nssSlot_ResetDelay(nssToken->slot);
+ }
+ (void)nssToken_Destroy(nssToken);
+ }
+ /* force the slot info structures to properly reset */
+ (void)PK11_IsPresent(slot);
+ }
+ return slot;
+}
+
+/*
+ * given a module spec, find the slot in the module for it.
+ */
+PK11SlotInfo *
+secmod_FindSlotFromModuleSpec(const char *moduleSpec, SECMODModule *module)
+{
+ CK_SLOT_ID slot_id = secmod_GetSlotIDFromModuleSpec(moduleSpec, module);
+ if (slot_id == -1) {
+ return NULL;
+ }
+
+ return SECMOD_FindSlotByID(module, slot_id);
+}
+
+/*
+ * Open a new database using the softoken. The caller is responsible for making
+ * sure the module spec is correct and usable. The caller should ask for one
+ * new database per call if the caller wants to get meaningful information
+ * about the new database.
+ *
+ * moduleSpec is the same data that you would pass to softoken at
+ * initialization time under the 'tokens' options. For example, if you were
+ * to specify tokens=<0x4=[configdir='./mybackup' tokenDescription='Backup']>
+ * You would specify "configdir='./mybackup' tokenDescription='Backup'" as your
+ * module spec here. The slot ID will be calculated for you by
+ * SECMOD_OpenUserDB().
+ *
+ * Typical parameters here are configdir, tokenDescription and flags.
+ *
+ * a Full list is below:
+ *
+ *
+ * configDir - The location of the databases for this token. If configDir is
+ * not specified, and noCertDB and noKeyDB is not specified, the load
+ * will fail.
+ * certPrefix - Cert prefix for this token.
+ * keyPrefix - Prefix for the key database for this token. (if not specified,
+ * certPrefix will be used).
+ * tokenDescription - The label value for this token returned in the
+ * CK_TOKEN_INFO structure with an internationalize string (UTF8).
+ * This value will be truncated at 32 bytes (no NULL, partial UTF8
+ * characters dropped). You should specify a user friendly name here
+ * as this is the value the token will be referred to in most
+ * application UI's. You should make sure tokenDescription is unique.
+ * slotDescription - The slotDescription value for this token returned
+ * in the CK_SLOT_INFO structure with an internationalize string
+ * (UTF8). This value will be truncated at 64 bytes (no NULL, partial
+ * UTF8 characters dropped). This name will not change after the
+ * database is closed. It should have some number to make this unique.
+ * minPWLen - minimum password length for this token.
+ * flags - comma separated list of flag values, parsed case-insensitive.
+ * Valid flags are:
+ * readOnly - Databases should be opened read only.
+ * noCertDB - Don't try to open a certificate database.
+ * noKeyDB - Don't try to open a key database.
+ * forceOpen - Don't fail to initialize the token if the
+ * databases could not be opened.
+ * passwordRequired - zero length passwords are not acceptable
+ * (valid only if there is a keyDB).
+ * optimizeSpace - allocate smaller hash tables and lock tables.
+ * When this flag is not specified, Softoken will allocate
+ * large tables to prevent lock contention.
+ */
+PK11SlotInfo *
+SECMOD_OpenUserDB(const char *moduleSpec)
+{
+ SECMODModule *mod;
+ SECMODConfigList *conflist = NULL;
+ int count = 0;
+
+ if (moduleSpec == NULL) {
+ return NULL;
+ }
+
+ /* NOTE: unlike most PK11 function, this does not return a reference
+ * to the module */
+ mod = SECMOD_GetInternalModule();
+ if (!mod) {
+ /* shouldn't happen */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ /* make sure we don't open the same database twice. We only understand
+ * the moduleSpec for internal databases well enough to do this, so only
+ * do this in OpenUserDB */
+ conflist = secmod_GetConfigList(mod->isFIPS, mod->libraryParams, &count);
+ if (conflist) {
+ PK11SlotInfo *slot = NULL;
+ if (secmod_MatchConfigList(moduleSpec, conflist, count)) {
+ slot = secmod_FindSlotFromModuleSpec(moduleSpec, mod);
+ }
+ secmod_FreeConfigList(conflist, count);
+ if (slot) {
+ return slot;
+ }
+ }
+ return SECMOD_OpenNewSlot(mod, moduleSpec);
+}
+
+/*
+ * close an already opened user database. NOTE: the database must be
+ * in the internal token, and must be one created with SECMOD_OpenUserDB().
+ * Once the database is closed, the slot will remain as an empty slot
+ * until it's used again with SECMOD_OpenUserDB() or SECMOD_OpenNewSlot().
+ */
+SECStatus
+SECMOD_CloseUserDB(PK11SlotInfo *slot)
+{
+ SECStatus rv;
+ char *sendSpec;
+
+ sendSpec = PR_smprintf("tokens=[0x%x=<>]", slot->slotID);
+ if (sendSpec == NULL) {
+ /* PR_smprintf does not set no memory error */
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ rv = secmod_UserDBOp(slot, CKO_NSS_DELSLOT, sendSpec);
+ PR_smprintf_free(sendSpec);
+ /* if we are in the delay period for the "isPresent" call, reset
+ * the delay since we know things have probably changed... */
+ NSSToken *nssToken = PK11Slot_GetNSSToken(slot);
+ if (nssToken) {
+ if (nssToken->slot) {
+ nssSlot_ResetDelay(nssToken->slot);
+ }
+ (void)nssToken_Destroy(nssToken);
+ /* force the slot info structures to properly reset */
+ (void)PK11_IsPresent(slot);
+ }
+ return rv;
+}
+
+/*
+ * Restart PKCS #11 modules after a fork(). See secmod.h for more information.
+ */
+SECStatus
+SECMOD_RestartModules(PRBool force)
+{
+ SECMODModuleList *mlp;
+ SECStatus rrv = SECSuccess;
+ int lastError = 0;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+
+ /* Only need to restart the PKCS #11 modules that were initialized */
+ SECMOD_GetReadLock(moduleLock);
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ SECMODModule *mod = mlp->module;
+ CK_ULONG count;
+ SECStatus rv;
+ int i;
+
+ /* If the module needs to be reset, do so */
+ if (force || (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &count) != CKR_OK)) {
+ PRBool alreadyLoaded;
+ /* first call Finalize. This is not required by PKCS #11, but some
+ * older modules require it, and it doesn't hurt (compliant modules
+ * will return CKR_NOT_INITIALIZED */
+ (void)PK11_GETTAB(mod)->C_Finalize(NULL);
+ /* now initialize the module, this function reinitializes
+ * a module in place, preserving existing slots (even if they
+ * no longer exist) */
+ rv = secmod_ModuleInit(mod, NULL, &alreadyLoaded);
+ if (rv != SECSuccess) {
+ /* save the last error code */
+ lastError = PORT_GetError();
+ rrv = rv;
+ /* couldn't reinit the module, disable all its slots */
+ for (i = 0; i < mod->slotCount; i++) {
+ mod->slots[i]->disabled = PR_TRUE;
+ mod->slots[i]->reason = PK11_DIS_COULD_NOT_INIT_TOKEN;
+ }
+ continue;
+ }
+ for (i = 0; i < mod->slotCount; i++) {
+ /* get new token sessions, bump the series up so that
+ * we refresh other old sessions. This will tell much of
+ * NSS to flush cached handles it may hold as well */
+ rv = PK11_InitToken(mod->slots[i], PR_TRUE);
+ /* PK11_InitToken could fail if the slot isn't present.
+ * If it is present, though, something is wrong and we should
+ * disable the slot and let the caller know. */
+ if (rv != SECSuccess && PK11_IsPresent(mod->slots[i])) {
+ /* save the last error code */
+ lastError = PORT_GetError();
+ rrv = rv;
+ /* disable the token */
+ mod->slots[i]->disabled = PR_TRUE;
+ mod->slots[i]->reason = PK11_DIS_COULD_NOT_INIT_TOKEN;
+ }
+ }
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ /*
+ * on multiple failures, we are only returning the lastError. The caller
+ * can determine which slots are bad by calling PK11_IsDisabled().
+ */
+ if (rrv != SECSuccess) {
+ /* restore the last error code */
+ PORT_SetError(lastError);
+ }
+
+ return rrv;
+}