/* * load a new module into our address space and initialize it. */ SECStatus secmod_LoadPKCS11Module(SECMODModule *mod, SECMODModule **oldModule) { PRLibrary *library = NULL; CK_C_GetFunctionList entry = NULL; CK_INFO info; CK_ULONG slotCount = 0; SECStatus rv; PRBool alreadyLoaded = PR_FALSE; char *disableUnload = NULL; if (mod->loaded) return SECSuccess; /* internal modules get loaded from their internal list */ if (mod->internal && (mod->dllName == NULL)) { #ifdef NSS_TEST_BUILD entry = (CK_C_GetFunctionList)NSC_GetFunctionList; #else /* * Loads softoken as a dynamic library, * even though the rest of NSS assumes this as the "internal" module. */ if (!softokenLib && PR_SUCCESS != PR_CallOnce(&loadSoftokenOnce, &softoken_LoadDSO)) return SECFailure; PR_ATOMIC_INCREMENT(&softokenLoadCount); if (mod->isFIPS) { entry = (CK_C_GetFunctionList) PR_FindSymbol(softokenLib, "FC_GetFunctionList"); } else { entry = (CK_C_GetFunctionList) PR_FindSymbol(softokenLib, "NSC_GetFunctionList"); } if (!entry) return SECFailure; #endif if (mod->isModuleDB) { mod->moduleDBFunc = (CK_C_GetFunctionList) #ifdef NSS_TEST_BUILD NSC_ModuleDBFunc; #else PR_FindSymbol(softokenLib, "NSC_ModuleDBFunc"); #endif } if (mod->moduleDBOnly) { mod->loaded = PR_TRUE; return SECSuccess; } } else { /* Not internal, load the DLL and look up C_GetFunctionList */ if (mod->dllName == NULL) { return SECFailure; } /* load the library. If this succeeds, then we have to remember to * unload the library if anything goes wrong from here on out... */ library = PR_LoadLibrary(mod->dllName); mod->library = (void *)library; if (library == NULL) { return SECFailure; } /* * now we need to get the entry point to find the function pointers */ if (!mod->moduleDBOnly) { entry = (CK_C_GetFunctionList) PR_FindSymbol(library, "C_GetFunctionList"); } if (mod->isModuleDB) { mod->moduleDBFunc = (void *) PR_FindSymbol(library, "NSS_ReturnModuleSpecData"); } if (mod->moduleDBFunc == NULL) mod->isModuleDB = PR_FALSE; if (entry == NULL) { if (mod->isModuleDB) { mod->loaded = PR_TRUE; mod->moduleDBOnly = PR_TRUE; return SECSuccess; } PR_UnloadLibrary(library); return SECFailure; } } /* * We need to get the function list */ if ((*entry)((CK_FUNCTION_LIST_PTR *)&mod->functionList) != CKR_OK) goto fail; #ifdef DEBUG_MODULE if (PR_TRUE) { modToDBG = PR_GetEnvSecure("NSS_DEBUG_PKCS11_MODULE"); if (modToDBG && strcmp(mod->commonName, modToDBG) == 0) { mod->functionList = (void *)nss_InsertDeviceLog( (CK_FUNCTION_LIST_PTR)mod->functionList); } } #endif mod->isThreadSafe = PR_TRUE; /* Now we initialize the module */ rv = secmod_ModuleInit(mod, oldModule, &alreadyLoaded); if (rv != SECSuccess) { goto fail; } /* module has been reloaded, this module itself is done, * return to the caller */ if (mod->functionList == NULL) { mod->loaded = PR_TRUE; /* technically the module is loaded.. */ return SECSuccess; } /* check the version number */ if (PK11_GETTAB(mod)->C_GetInfo(&info) != CKR_OK) goto fail2; if (info.cryptokiVersion.major != 2) goto fail2; /* all 2.0 are a priori *not* thread safe */ if (info.cryptokiVersion.minor < 1) { if (!loadSingleThreadedModules) { PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11); goto fail2; } else { mod->isThreadSafe = PR_FALSE; } } mod->cryptokiVersion = info.cryptokiVersion; /* If we don't have a common name, get it from the PKCS 11 module */ if ((mod->commonName == NULL) || (mod->commonName[0] == 0)) { mod->commonName = PK11_MakeString(mod->arena, NULL, (char *)info.libraryDescription, sizeof(info.libraryDescription)); if (mod->commonName == NULL) goto fail2; } /* initialize the Slots */ if (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &slotCount) == CKR_OK) { CK_SLOT_ID *slotIDs; int i; CK_RV crv; mod->slots = (PK11SlotInfo **)PORT_ArenaAlloc(mod->arena, sizeof(PK11SlotInfo *) * slotCount); if (mod->slots == NULL) goto fail2; slotIDs = (CK_SLOT_ID *)PORT_Alloc(sizeof(CK_SLOT_ID) * slotCount); if (slotIDs == NULL) { goto fail2; } crv = PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, slotIDs, &slotCount); if (crv != CKR_OK) { PORT_Free(slotIDs); goto fail2; } /* Initialize each slot */ for (i = 0; i < (int)slotCount; i++) { mod->slots[i] = PK11_NewSlotInfo(mod); PK11_InitSlot(mod, slotIDs[i], mod->slots[i]); /* look down the slot info table */ PK11_LoadSlotList(mod->slots[i], mod->slotInfo, mod->slotInfoCount); SECMOD_SetRootCerts(mod->slots[i], mod); /* explicitly mark the internal slot as such if IsInternalKeySlot() * is set */ if (secmod_IsInternalKeySlot(mod) && (i == (mod->isFIPS ? 0 : 1))) { pk11_SetInternalKeySlotIfFirst(mod->slots[i]); } } mod->slotCount = slotCount; mod->slotInfoCount = 0; PORT_Free(slotIDs); } mod->loaded = PR_TRUE; mod->moduleID = nextModuleID++; return SECSuccess; fail2: if (enforceAlreadyInitializedError || (!alreadyLoaded)) { PK11_GETTAB(mod)->C_Finalize(NULL); } fail: mod->functionList = NULL; disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD"); if (library && !disableUnload) { PR_UnloadLibrary(library); } return SECFailure; }
/* * 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. */ 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; }