Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | /* |
michael@0 | 5 | * Initialize the PCKS 11 subsystem |
michael@0 | 6 | */ |
michael@0 | 7 | #include "seccomon.h" |
michael@0 | 8 | #include "secmod.h" |
michael@0 | 9 | #include "nssilock.h" |
michael@0 | 10 | #include "secmodi.h" |
michael@0 | 11 | #include "secmodti.h" |
michael@0 | 12 | #include "pk11func.h" |
michael@0 | 13 | #include "pki3hack.h" |
michael@0 | 14 | #include "secerr.h" |
michael@0 | 15 | #include "dev.h" |
michael@0 | 16 | #include "utilpars.h" |
michael@0 | 17 | |
michael@0 | 18 | /* these are for displaying error messages */ |
michael@0 | 19 | |
michael@0 | 20 | static SECMODModuleList *modules = NULL; |
michael@0 | 21 | static SECMODModuleList *modulesDB = NULL; |
michael@0 | 22 | static SECMODModuleList *modulesUnload = NULL; |
michael@0 | 23 | static SECMODModule *internalModule = NULL; |
michael@0 | 24 | static SECMODModule *defaultDBModule = NULL; |
michael@0 | 25 | static SECMODModule *pendingModule = NULL; |
michael@0 | 26 | static SECMODListLock *moduleLock = NULL; |
michael@0 | 27 | |
michael@0 | 28 | int secmod_PrivateModuleCount = 0; |
michael@0 | 29 | |
michael@0 | 30 | extern const PK11DefaultArrayEntry PK11_DefaultArray[]; |
michael@0 | 31 | extern const int num_pk11_default_mechanisms; |
michael@0 | 32 | |
michael@0 | 33 | |
michael@0 | 34 | void |
michael@0 | 35 | SECMOD_Init() |
michael@0 | 36 | { |
michael@0 | 37 | /* don't initialize twice */ |
michael@0 | 38 | if (moduleLock) return; |
michael@0 | 39 | |
michael@0 | 40 | moduleLock = SECMOD_NewListLock(); |
michael@0 | 41 | PK11_InitSlotLists(); |
michael@0 | 42 | } |
michael@0 | 43 | |
michael@0 | 44 | |
michael@0 | 45 | SECStatus |
michael@0 | 46 | SECMOD_Shutdown() |
michael@0 | 47 | { |
michael@0 | 48 | /* destroy the lock */ |
michael@0 | 49 | if (moduleLock) { |
michael@0 | 50 | SECMOD_DestroyListLock(moduleLock); |
michael@0 | 51 | moduleLock = NULL; |
michael@0 | 52 | } |
michael@0 | 53 | /* free the internal module */ |
michael@0 | 54 | if (internalModule) { |
michael@0 | 55 | SECMOD_DestroyModule(internalModule); |
michael@0 | 56 | internalModule = NULL; |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | /* free the default database module */ |
michael@0 | 60 | if (defaultDBModule) { |
michael@0 | 61 | SECMOD_DestroyModule(defaultDBModule); |
michael@0 | 62 | defaultDBModule = NULL; |
michael@0 | 63 | } |
michael@0 | 64 | |
michael@0 | 65 | /* destroy the list */ |
michael@0 | 66 | if (modules) { |
michael@0 | 67 | SECMOD_DestroyModuleList(modules); |
michael@0 | 68 | modules = NULL; |
michael@0 | 69 | } |
michael@0 | 70 | |
michael@0 | 71 | if (modulesDB) { |
michael@0 | 72 | SECMOD_DestroyModuleList(modulesDB); |
michael@0 | 73 | modulesDB = NULL; |
michael@0 | 74 | } |
michael@0 | 75 | |
michael@0 | 76 | if (modulesUnload) { |
michael@0 | 77 | SECMOD_DestroyModuleList(modulesUnload); |
michael@0 | 78 | modulesUnload = NULL; |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | /* make all the slots and the lists go away */ |
michael@0 | 82 | PK11_DestroySlotLists(); |
michael@0 | 83 | |
michael@0 | 84 | nss_DumpModuleLog(); |
michael@0 | 85 | |
michael@0 | 86 | #ifdef DEBUG |
michael@0 | 87 | if (PR_GetEnv("NSS_STRICT_SHUTDOWN")) { |
michael@0 | 88 | PORT_Assert(secmod_PrivateModuleCount == 0); |
michael@0 | 89 | } |
michael@0 | 90 | #endif |
michael@0 | 91 | if (secmod_PrivateModuleCount) { |
michael@0 | 92 | PORT_SetError(SEC_ERROR_BUSY); |
michael@0 | 93 | return SECFailure; |
michael@0 | 94 | } |
michael@0 | 95 | return SECSuccess; |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | |
michael@0 | 99 | /* |
michael@0 | 100 | * retrieve the internal module |
michael@0 | 101 | */ |
michael@0 | 102 | SECMODModule * |
michael@0 | 103 | SECMOD_GetInternalModule(void) |
michael@0 | 104 | { |
michael@0 | 105 | return internalModule; |
michael@0 | 106 | } |
michael@0 | 107 | |
michael@0 | 108 | |
michael@0 | 109 | SECStatus |
michael@0 | 110 | secmod_AddModuleToList(SECMODModuleList **moduleList,SECMODModule *newModule) |
michael@0 | 111 | { |
michael@0 | 112 | SECMODModuleList *mlp, *newListElement, *last = NULL; |
michael@0 | 113 | |
michael@0 | 114 | newListElement = SECMOD_NewModuleListElement(); |
michael@0 | 115 | if (newListElement == NULL) { |
michael@0 | 116 | return SECFailure; |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | newListElement->module = SECMOD_ReferenceModule(newModule); |
michael@0 | 120 | |
michael@0 | 121 | SECMOD_GetWriteLock(moduleLock); |
michael@0 | 122 | /* Added it to the end (This is very inefficient, but Adding a module |
michael@0 | 123 | * on the fly should happen maybe 2-3 times through the life this program |
michael@0 | 124 | * on a given computer, and this list should be *SHORT*. */ |
michael@0 | 125 | for(mlp = *moduleList; mlp != NULL; mlp = mlp->next) { |
michael@0 | 126 | last = mlp; |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | if (last == NULL) { |
michael@0 | 130 | *moduleList = newListElement; |
michael@0 | 131 | } else { |
michael@0 | 132 | SECMOD_AddList(last,newListElement,NULL); |
michael@0 | 133 | } |
michael@0 | 134 | SECMOD_ReleaseWriteLock(moduleLock); |
michael@0 | 135 | return SECSuccess; |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | SECStatus |
michael@0 | 139 | SECMOD_AddModuleToList(SECMODModule *newModule) |
michael@0 | 140 | { |
michael@0 | 141 | if (newModule->internal && !internalModule) { |
michael@0 | 142 | internalModule = SECMOD_ReferenceModule(newModule); |
michael@0 | 143 | } |
michael@0 | 144 | return secmod_AddModuleToList(&modules,newModule); |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | SECStatus |
michael@0 | 148 | SECMOD_AddModuleToDBOnlyList(SECMODModule *newModule) |
michael@0 | 149 | { |
michael@0 | 150 | if (defaultDBModule && SECMOD_GetDefaultModDBFlag(newModule)) { |
michael@0 | 151 | SECMOD_DestroyModule(defaultDBModule); |
michael@0 | 152 | defaultDBModule = SECMOD_ReferenceModule(newModule); |
michael@0 | 153 | } else if (defaultDBModule == NULL) { |
michael@0 | 154 | defaultDBModule = SECMOD_ReferenceModule(newModule); |
michael@0 | 155 | } |
michael@0 | 156 | return secmod_AddModuleToList(&modulesDB,newModule); |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | SECStatus |
michael@0 | 160 | SECMOD_AddModuleToUnloadList(SECMODModule *newModule) |
michael@0 | 161 | { |
michael@0 | 162 | return secmod_AddModuleToList(&modulesUnload,newModule); |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | /* |
michael@0 | 166 | * get the list of PKCS11 modules that are available. |
michael@0 | 167 | */ |
michael@0 | 168 | SECMODModuleList * SECMOD_GetDefaultModuleList() { return modules; } |
michael@0 | 169 | SECMODModuleList *SECMOD_GetDeadModuleList() { return modulesUnload; } |
michael@0 | 170 | SECMODModuleList *SECMOD_GetDBModuleList() { return modulesDB; } |
michael@0 | 171 | |
michael@0 | 172 | /* |
michael@0 | 173 | * This lock protects the global module lists. |
michael@0 | 174 | * it also protects changes to the slot array (module->slots[]) and slot count |
michael@0 | 175 | * (module->slotCount) in each module. It is a read/write lock with multiple |
michael@0 | 176 | * readers or one writer. Writes are uncommon. |
michael@0 | 177 | * Because of legacy considerations protection of the slot array and count is |
michael@0 | 178 | * only necessary in applications if the application calls |
michael@0 | 179 | * SECMOD_UpdateSlotList() or SECMOD_WaitForAnyTokenEvent(), though all new |
michael@0 | 180 | * applications are encouraged to acquire this lock when reading the |
michael@0 | 181 | * slot array information directly. |
michael@0 | 182 | */ |
michael@0 | 183 | SECMODListLock *SECMOD_GetDefaultModuleListLock() { return moduleLock; } |
michael@0 | 184 | |
michael@0 | 185 | |
michael@0 | 186 | |
michael@0 | 187 | /* |
michael@0 | 188 | * find a module by name, and add a reference to it. |
michael@0 | 189 | * return that module. |
michael@0 | 190 | */ |
michael@0 | 191 | SECMODModule * |
michael@0 | 192 | SECMOD_FindModule(const char *name) |
michael@0 | 193 | { |
michael@0 | 194 | SECMODModuleList *mlp; |
michael@0 | 195 | SECMODModule *module = NULL; |
michael@0 | 196 | |
michael@0 | 197 | if (!moduleLock) { |
michael@0 | 198 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 199 | return module; |
michael@0 | 200 | } |
michael@0 | 201 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 202 | for(mlp = modules; mlp != NULL; mlp = mlp->next) { |
michael@0 | 203 | if (PORT_Strcmp(name,mlp->module->commonName) == 0) { |
michael@0 | 204 | module = mlp->module; |
michael@0 | 205 | SECMOD_ReferenceModule(module); |
michael@0 | 206 | break; |
michael@0 | 207 | } |
michael@0 | 208 | } |
michael@0 | 209 | if (module) { |
michael@0 | 210 | goto found; |
michael@0 | 211 | } |
michael@0 | 212 | for(mlp = modulesUnload; mlp != NULL; mlp = mlp->next) { |
michael@0 | 213 | if (PORT_Strcmp(name,mlp->module->commonName) == 0) { |
michael@0 | 214 | module = mlp->module; |
michael@0 | 215 | SECMOD_ReferenceModule(module); |
michael@0 | 216 | break; |
michael@0 | 217 | } |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | found: |
michael@0 | 221 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 222 | |
michael@0 | 223 | return module; |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | /* |
michael@0 | 227 | * find a module by ID, and add a reference to it. |
michael@0 | 228 | * return that module. |
michael@0 | 229 | */ |
michael@0 | 230 | SECMODModule * |
michael@0 | 231 | SECMOD_FindModuleByID(SECMODModuleID id) |
michael@0 | 232 | { |
michael@0 | 233 | SECMODModuleList *mlp; |
michael@0 | 234 | SECMODModule *module = NULL; |
michael@0 | 235 | |
michael@0 | 236 | if (!moduleLock) { |
michael@0 | 237 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 238 | return module; |
michael@0 | 239 | } |
michael@0 | 240 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 241 | for(mlp = modules; mlp != NULL; mlp = mlp->next) { |
michael@0 | 242 | if (id == mlp->module->moduleID) { |
michael@0 | 243 | module = mlp->module; |
michael@0 | 244 | SECMOD_ReferenceModule(module); |
michael@0 | 245 | break; |
michael@0 | 246 | } |
michael@0 | 247 | } |
michael@0 | 248 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 249 | if (module == NULL) { |
michael@0 | 250 | PORT_SetError(SEC_ERROR_NO_MODULE); |
michael@0 | 251 | } |
michael@0 | 252 | return module; |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | /* |
michael@0 | 256 | * find the function pointer. |
michael@0 | 257 | */ |
michael@0 | 258 | SECMODModule * |
michael@0 | 259 | secmod_FindModuleByFuncPtr(void *funcPtr) |
michael@0 | 260 | { |
michael@0 | 261 | SECMODModuleList *mlp; |
michael@0 | 262 | SECMODModule *module = NULL; |
michael@0 | 263 | |
michael@0 | 264 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 265 | for(mlp = modules; mlp != NULL; mlp = mlp->next) { |
michael@0 | 266 | /* paranoia, shouldn't ever happen */ |
michael@0 | 267 | if (!mlp->module) { |
michael@0 | 268 | continue; |
michael@0 | 269 | } |
michael@0 | 270 | if (funcPtr == mlp->module->functionList) { |
michael@0 | 271 | module = mlp->module; |
michael@0 | 272 | SECMOD_ReferenceModule(module); |
michael@0 | 273 | break; |
michael@0 | 274 | } |
michael@0 | 275 | } |
michael@0 | 276 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 277 | if (module == NULL) { |
michael@0 | 278 | PORT_SetError(SEC_ERROR_NO_MODULE); |
michael@0 | 279 | } |
michael@0 | 280 | return module; |
michael@0 | 281 | } |
michael@0 | 282 | |
michael@0 | 283 | /* |
michael@0 | 284 | * Find the Slot based on ID and the module. |
michael@0 | 285 | */ |
michael@0 | 286 | PK11SlotInfo * |
michael@0 | 287 | SECMOD_FindSlotByID(SECMODModule *module, CK_SLOT_ID slotID) |
michael@0 | 288 | { |
michael@0 | 289 | int i; |
michael@0 | 290 | PK11SlotInfo *slot = NULL; |
michael@0 | 291 | |
michael@0 | 292 | if (!moduleLock) { |
michael@0 | 293 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 294 | return slot; |
michael@0 | 295 | } |
michael@0 | 296 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 297 | for (i=0; i < module->slotCount; i++) { |
michael@0 | 298 | PK11SlotInfo *cSlot = module->slots[i]; |
michael@0 | 299 | |
michael@0 | 300 | if (cSlot->slotID == slotID) { |
michael@0 | 301 | slot = PK11_ReferenceSlot(cSlot); |
michael@0 | 302 | break; |
michael@0 | 303 | } |
michael@0 | 304 | } |
michael@0 | 305 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 306 | |
michael@0 | 307 | if (slot == NULL) { |
michael@0 | 308 | PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED); |
michael@0 | 309 | } |
michael@0 | 310 | return slot; |
michael@0 | 311 | } |
michael@0 | 312 | |
michael@0 | 313 | /* |
michael@0 | 314 | * lookup the Slot module based on it's module ID and slot ID. |
michael@0 | 315 | */ |
michael@0 | 316 | PK11SlotInfo * |
michael@0 | 317 | SECMOD_LookupSlot(SECMODModuleID moduleID,CK_SLOT_ID slotID) |
michael@0 | 318 | { |
michael@0 | 319 | SECMODModule *module; |
michael@0 | 320 | PK11SlotInfo *slot; |
michael@0 | 321 | |
michael@0 | 322 | module = SECMOD_FindModuleByID(moduleID); |
michael@0 | 323 | if (module == NULL) return NULL; |
michael@0 | 324 | |
michael@0 | 325 | slot = SECMOD_FindSlotByID(module, slotID); |
michael@0 | 326 | SECMOD_DestroyModule(module); |
michael@0 | 327 | return slot; |
michael@0 | 328 | } |
michael@0 | 329 | |
michael@0 | 330 | |
michael@0 | 331 | /* |
michael@0 | 332 | * find a module by name or module pointer and delete it off the module list. |
michael@0 | 333 | * optionally remove it from secmod.db. |
michael@0 | 334 | */ |
michael@0 | 335 | SECStatus |
michael@0 | 336 | SECMOD_DeleteModuleEx(const char *name, SECMODModule *mod, |
michael@0 | 337 | int *type, PRBool permdb) |
michael@0 | 338 | { |
michael@0 | 339 | SECMODModuleList *mlp; |
michael@0 | 340 | SECMODModuleList **mlpp; |
michael@0 | 341 | SECStatus rv = SECFailure; |
michael@0 | 342 | |
michael@0 | 343 | if (!moduleLock) { |
michael@0 | 344 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 345 | return rv; |
michael@0 | 346 | } |
michael@0 | 347 | |
michael@0 | 348 | *type = SECMOD_EXTERNAL; |
michael@0 | 349 | |
michael@0 | 350 | SECMOD_GetWriteLock(moduleLock); |
michael@0 | 351 | for (mlpp = &modules,mlp = modules; |
michael@0 | 352 | mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) { |
michael@0 | 353 | if ((name && (PORT_Strcmp(name,mlp->module->commonName) == 0)) || |
michael@0 | 354 | mod == mlp->module) { |
michael@0 | 355 | /* don't delete the internal module */ |
michael@0 | 356 | if (!mlp->module->internal) { |
michael@0 | 357 | SECMOD_RemoveList(mlpp,mlp); |
michael@0 | 358 | /* delete it after we release the lock */ |
michael@0 | 359 | rv = STAN_RemoveModuleFromDefaultTrustDomain(mlp->module); |
michael@0 | 360 | } else if (mlp->module->isFIPS) { |
michael@0 | 361 | *type = SECMOD_FIPS; |
michael@0 | 362 | } else { |
michael@0 | 363 | *type = SECMOD_INTERNAL; |
michael@0 | 364 | } |
michael@0 | 365 | break; |
michael@0 | 366 | } |
michael@0 | 367 | } |
michael@0 | 368 | if (mlp) { |
michael@0 | 369 | goto found; |
michael@0 | 370 | } |
michael@0 | 371 | /* not on the internal list, check the unload list */ |
michael@0 | 372 | for (mlpp = &modulesUnload,mlp = modulesUnload; |
michael@0 | 373 | mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) { |
michael@0 | 374 | if ((name && (PORT_Strcmp(name,mlp->module->commonName) == 0)) || |
michael@0 | 375 | mod == mlp->module) { |
michael@0 | 376 | /* don't delete the internal module */ |
michael@0 | 377 | if (!mlp->module->internal) { |
michael@0 | 378 | SECMOD_RemoveList(mlpp,mlp); |
michael@0 | 379 | rv = SECSuccess; |
michael@0 | 380 | } else if (mlp->module->isFIPS) { |
michael@0 | 381 | *type = SECMOD_FIPS; |
michael@0 | 382 | } else { |
michael@0 | 383 | *type = SECMOD_INTERNAL; |
michael@0 | 384 | } |
michael@0 | 385 | break; |
michael@0 | 386 | } |
michael@0 | 387 | } |
michael@0 | 388 | found: |
michael@0 | 389 | SECMOD_ReleaseWriteLock(moduleLock); |
michael@0 | 390 | |
michael@0 | 391 | |
michael@0 | 392 | if (rv == SECSuccess) { |
michael@0 | 393 | if (permdb) { |
michael@0 | 394 | SECMOD_DeletePermDB(mlp->module); |
michael@0 | 395 | } |
michael@0 | 396 | SECMOD_DestroyModuleListElement(mlp); |
michael@0 | 397 | } |
michael@0 | 398 | return rv; |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | /* |
michael@0 | 402 | * find a module by name and delete it off the module list |
michael@0 | 403 | */ |
michael@0 | 404 | SECStatus |
michael@0 | 405 | SECMOD_DeleteModule(const char *name, int *type) |
michael@0 | 406 | { |
michael@0 | 407 | return SECMOD_DeleteModuleEx(name, NULL, type, PR_TRUE); |
michael@0 | 408 | } |
michael@0 | 409 | |
michael@0 | 410 | /* |
michael@0 | 411 | * find a module by name and delete it off the module list |
michael@0 | 412 | */ |
michael@0 | 413 | SECStatus |
michael@0 | 414 | SECMOD_DeleteInternalModule(const char *name) |
michael@0 | 415 | { |
michael@0 | 416 | SECMODModuleList *mlp; |
michael@0 | 417 | SECMODModuleList **mlpp; |
michael@0 | 418 | SECStatus rv = SECFailure; |
michael@0 | 419 | |
michael@0 | 420 | if (pendingModule) { |
michael@0 | 421 | PORT_SetError(SEC_ERROR_MODULE_STUCK); |
michael@0 | 422 | return rv; |
michael@0 | 423 | } |
michael@0 | 424 | if (!moduleLock) { |
michael@0 | 425 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 426 | return rv; |
michael@0 | 427 | } |
michael@0 | 428 | |
michael@0 | 429 | SECMOD_GetWriteLock(moduleLock); |
michael@0 | 430 | for(mlpp = &modules,mlp = modules; |
michael@0 | 431 | mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) { |
michael@0 | 432 | if (PORT_Strcmp(name,mlp->module->commonName) == 0) { |
michael@0 | 433 | /* don't delete the internal module */ |
michael@0 | 434 | if (mlp->module->internal) { |
michael@0 | 435 | SECMOD_RemoveList(mlpp,mlp); |
michael@0 | 436 | rv = STAN_RemoveModuleFromDefaultTrustDomain(mlp->module); |
michael@0 | 437 | } |
michael@0 | 438 | break; |
michael@0 | 439 | } |
michael@0 | 440 | } |
michael@0 | 441 | SECMOD_ReleaseWriteLock(moduleLock); |
michael@0 | 442 | |
michael@0 | 443 | if (rv == SECSuccess) { |
michael@0 | 444 | SECMODModule *newModule,*oldModule; |
michael@0 | 445 | |
michael@0 | 446 | if (mlp->module->isFIPS) { |
michael@0 | 447 | newModule = SECMOD_CreateModule(NULL, SECMOD_INT_NAME, |
michael@0 | 448 | NULL, SECMOD_INT_FLAGS); |
michael@0 | 449 | } else { |
michael@0 | 450 | newModule = SECMOD_CreateModule(NULL, SECMOD_FIPS_NAME, |
michael@0 | 451 | NULL, SECMOD_FIPS_FLAGS); |
michael@0 | 452 | } |
michael@0 | 453 | if (newModule) { |
michael@0 | 454 | PK11SlotInfo *slot; |
michael@0 | 455 | newModule->libraryParams = |
michael@0 | 456 | PORT_ArenaStrdup(newModule->arena,mlp->module->libraryParams); |
michael@0 | 457 | /* if an explicit internal key slot has been set, reset it */ |
michael@0 | 458 | slot = pk11_SwapInternalKeySlot(NULL); |
michael@0 | 459 | if (slot) { |
michael@0 | 460 | secmod_SetInternalKeySlotFlag(newModule, PR_TRUE); |
michael@0 | 461 | } |
michael@0 | 462 | rv = SECMOD_AddModule(newModule); |
michael@0 | 463 | if (rv != SECSuccess) { |
michael@0 | 464 | /* load failed, restore the internal key slot */ |
michael@0 | 465 | pk11_SetInternalKeySlot(slot); |
michael@0 | 466 | SECMOD_DestroyModule(newModule); |
michael@0 | 467 | newModule = NULL; |
michael@0 | 468 | } |
michael@0 | 469 | /* free the old explicit internal key slot, we now have a new one */ |
michael@0 | 470 | if (slot) { |
michael@0 | 471 | PK11_FreeSlot(slot); |
michael@0 | 472 | } |
michael@0 | 473 | } |
michael@0 | 474 | if (newModule == NULL) { |
michael@0 | 475 | SECMODModuleList *last = NULL,*mlp2; |
michael@0 | 476 | /* we're in pretty deep trouble if this happens...Security |
michael@0 | 477 | * not going to work well... try to put the old module back on |
michael@0 | 478 | * the list */ |
michael@0 | 479 | SECMOD_GetWriteLock(moduleLock); |
michael@0 | 480 | for(mlp2 = modules; mlp2 != NULL; mlp2 = mlp->next) { |
michael@0 | 481 | last = mlp2; |
michael@0 | 482 | } |
michael@0 | 483 | |
michael@0 | 484 | if (last == NULL) { |
michael@0 | 485 | modules = mlp; |
michael@0 | 486 | } else { |
michael@0 | 487 | SECMOD_AddList(last,mlp,NULL); |
michael@0 | 488 | } |
michael@0 | 489 | SECMOD_ReleaseWriteLock(moduleLock); |
michael@0 | 490 | return SECFailure; |
michael@0 | 491 | } |
michael@0 | 492 | pendingModule = oldModule = internalModule; |
michael@0 | 493 | internalModule = NULL; |
michael@0 | 494 | SECMOD_DestroyModule(oldModule); |
michael@0 | 495 | SECMOD_DeletePermDB(mlp->module); |
michael@0 | 496 | SECMOD_DestroyModuleListElement(mlp); |
michael@0 | 497 | internalModule = newModule; /* adopt the module */ |
michael@0 | 498 | } |
michael@0 | 499 | return rv; |
michael@0 | 500 | } |
michael@0 | 501 | |
michael@0 | 502 | SECStatus |
michael@0 | 503 | SECMOD_AddModule(SECMODModule *newModule) |
michael@0 | 504 | { |
michael@0 | 505 | SECStatus rv; |
michael@0 | 506 | SECMODModule *oldModule; |
michael@0 | 507 | |
michael@0 | 508 | /* Test if a module w/ the same name already exists */ |
michael@0 | 509 | /* and return SECWouldBlock if so. */ |
michael@0 | 510 | /* We should probably add a new return value such as */ |
michael@0 | 511 | /* SECDublicateModule, but to minimize ripples, I'll */ |
michael@0 | 512 | /* give SECWouldBlock a new meaning */ |
michael@0 | 513 | if ((oldModule = SECMOD_FindModule(newModule->commonName)) != NULL) { |
michael@0 | 514 | SECMOD_DestroyModule(oldModule); |
michael@0 | 515 | return SECWouldBlock; |
michael@0 | 516 | /* module already exists. */ |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | rv = secmod_LoadPKCS11Module(newModule, NULL); |
michael@0 | 520 | if (rv != SECSuccess) { |
michael@0 | 521 | return rv; |
michael@0 | 522 | } |
michael@0 | 523 | |
michael@0 | 524 | if (newModule->parent == NULL) { |
michael@0 | 525 | newModule->parent = SECMOD_ReferenceModule(defaultDBModule); |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | SECMOD_AddPermDB(newModule); |
michael@0 | 529 | SECMOD_AddModuleToList(newModule); |
michael@0 | 530 | |
michael@0 | 531 | rv = STAN_AddModuleToDefaultTrustDomain(newModule); |
michael@0 | 532 | |
michael@0 | 533 | return rv; |
michael@0 | 534 | } |
michael@0 | 535 | |
michael@0 | 536 | PK11SlotInfo * |
michael@0 | 537 | SECMOD_FindSlot(SECMODModule *module,const char *name) |
michael@0 | 538 | { |
michael@0 | 539 | int i; |
michael@0 | 540 | char *string; |
michael@0 | 541 | PK11SlotInfo *retSlot = NULL; |
michael@0 | 542 | |
michael@0 | 543 | if (!moduleLock) { |
michael@0 | 544 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 545 | return retSlot; |
michael@0 | 546 | } |
michael@0 | 547 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 548 | for (i=0; i < module->slotCount; i++) { |
michael@0 | 549 | PK11SlotInfo *slot = module->slots[i]; |
michael@0 | 550 | |
michael@0 | 551 | if (PK11_IsPresent(slot)) { |
michael@0 | 552 | string = PK11_GetTokenName(slot); |
michael@0 | 553 | } else { |
michael@0 | 554 | string = PK11_GetSlotName(slot); |
michael@0 | 555 | } |
michael@0 | 556 | if (PORT_Strcmp(name,string) == 0) { |
michael@0 | 557 | retSlot = PK11_ReferenceSlot(slot); |
michael@0 | 558 | break; |
michael@0 | 559 | } |
michael@0 | 560 | } |
michael@0 | 561 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 562 | |
michael@0 | 563 | if (retSlot == NULL) { |
michael@0 | 564 | PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED); |
michael@0 | 565 | } |
michael@0 | 566 | return retSlot; |
michael@0 | 567 | } |
michael@0 | 568 | |
michael@0 | 569 | SECStatus |
michael@0 | 570 | PK11_GetModInfo(SECMODModule *mod,CK_INFO *info) |
michael@0 | 571 | { |
michael@0 | 572 | CK_RV crv; |
michael@0 | 573 | |
michael@0 | 574 | if (mod->functionList == NULL) return SECFailure; |
michael@0 | 575 | crv = PK11_GETTAB(mod)->C_GetInfo(info); |
michael@0 | 576 | if (crv != CKR_OK) { |
michael@0 | 577 | PORT_SetError(PK11_MapError(crv)); |
michael@0 | 578 | } |
michael@0 | 579 | return (crv == CKR_OK) ? SECSuccess : SECFailure; |
michael@0 | 580 | } |
michael@0 | 581 | |
michael@0 | 582 | /* Determine if we have the FIP's module loaded as the default |
michael@0 | 583 | * module to trigger other bogus FIPS requirements in PKCS #12 and |
michael@0 | 584 | * SSL |
michael@0 | 585 | */ |
michael@0 | 586 | PRBool |
michael@0 | 587 | PK11_IsFIPS(void) |
michael@0 | 588 | { |
michael@0 | 589 | SECMODModule *mod = SECMOD_GetInternalModule(); |
michael@0 | 590 | |
michael@0 | 591 | if (mod && mod->internal) { |
michael@0 | 592 | return mod->isFIPS; |
michael@0 | 593 | } |
michael@0 | 594 | |
michael@0 | 595 | return PR_FALSE; |
michael@0 | 596 | } |
michael@0 | 597 | |
michael@0 | 598 | /* combines NewModule() & AddModule */ |
michael@0 | 599 | /* give a string for the module name & the full-path for the dll, */ |
michael@0 | 600 | /* installs the PKCS11 module & update registry */ |
michael@0 | 601 | SECStatus |
michael@0 | 602 | SECMOD_AddNewModuleEx(const char* moduleName, const char* dllPath, |
michael@0 | 603 | unsigned long defaultMechanismFlags, |
michael@0 | 604 | unsigned long cipherEnableFlags, |
michael@0 | 605 | char* modparms, char* nssparms) |
michael@0 | 606 | { |
michael@0 | 607 | SECMODModule *module; |
michael@0 | 608 | SECStatus result = SECFailure; |
michael@0 | 609 | int s,i; |
michael@0 | 610 | PK11SlotInfo* slot; |
michael@0 | 611 | |
michael@0 | 612 | PR_SetErrorText(0, NULL); |
michael@0 | 613 | if (!moduleLock) { |
michael@0 | 614 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 615 | return result; |
michael@0 | 616 | } |
michael@0 | 617 | |
michael@0 | 618 | module = SECMOD_CreateModule(dllPath, moduleName, modparms, nssparms); |
michael@0 | 619 | |
michael@0 | 620 | if (module == NULL) { |
michael@0 | 621 | return result; |
michael@0 | 622 | } |
michael@0 | 623 | |
michael@0 | 624 | if (module->dllName != NULL) { |
michael@0 | 625 | if (module->dllName[0] != 0) { |
michael@0 | 626 | result = SECMOD_AddModule(module); |
michael@0 | 627 | if (result == SECSuccess) { |
michael@0 | 628 | /* turn on SSL cipher enable flags */ |
michael@0 | 629 | module->ssl[0] = cipherEnableFlags; |
michael@0 | 630 | |
michael@0 | 631 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 632 | /* check each slot to turn on appropriate mechanisms */ |
michael@0 | 633 | for (s = 0; s < module->slotCount; s++) { |
michael@0 | 634 | slot = (module->slots)[s]; |
michael@0 | 635 | /* for each possible mechanism */ |
michael@0 | 636 | for (i=0; i < num_pk11_default_mechanisms; i++) { |
michael@0 | 637 | /* we are told to turn it on by default ? */ |
michael@0 | 638 | PRBool add = |
michael@0 | 639 | (PK11_DefaultArray[i].flag & defaultMechanismFlags) ? |
michael@0 | 640 | PR_TRUE: PR_FALSE; |
michael@0 | 641 | result = PK11_UpdateSlotAttribute(slot, |
michael@0 | 642 | &(PK11_DefaultArray[i]), add); |
michael@0 | 643 | } /* for each mechanism */ |
michael@0 | 644 | /* disable each slot if the defaultFlags say so */ |
michael@0 | 645 | if (defaultMechanismFlags & PK11_DISABLE_FLAG) { |
michael@0 | 646 | PK11_UserDisableSlot(slot); |
michael@0 | 647 | } |
michael@0 | 648 | } /* for each slot of this module */ |
michael@0 | 649 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 650 | |
michael@0 | 651 | /* delete and re-add module in order to save changes |
michael@0 | 652 | * to the module */ |
michael@0 | 653 | result = SECMOD_UpdateModule(module); |
michael@0 | 654 | } |
michael@0 | 655 | } |
michael@0 | 656 | } |
michael@0 | 657 | SECMOD_DestroyModule(module); |
michael@0 | 658 | return result; |
michael@0 | 659 | } |
michael@0 | 660 | |
michael@0 | 661 | SECStatus |
michael@0 | 662 | SECMOD_AddNewModule(const char* moduleName, const char* dllPath, |
michael@0 | 663 | unsigned long defaultMechanismFlags, |
michael@0 | 664 | unsigned long cipherEnableFlags) |
michael@0 | 665 | { |
michael@0 | 666 | return SECMOD_AddNewModuleEx(moduleName, dllPath, defaultMechanismFlags, |
michael@0 | 667 | cipherEnableFlags, |
michael@0 | 668 | NULL, NULL); /* don't pass module or nss params */ |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | SECStatus |
michael@0 | 672 | SECMOD_UpdateModule(SECMODModule *module) |
michael@0 | 673 | { |
michael@0 | 674 | SECStatus result; |
michael@0 | 675 | |
michael@0 | 676 | result = SECMOD_DeletePermDB(module); |
michael@0 | 677 | |
michael@0 | 678 | if (result == SECSuccess) { |
michael@0 | 679 | result = SECMOD_AddPermDB(module); |
michael@0 | 680 | } |
michael@0 | 681 | return result; |
michael@0 | 682 | } |
michael@0 | 683 | |
michael@0 | 684 | /* Public & Internal(Security Library) representation of |
michael@0 | 685 | * encryption mechanism flags conversion */ |
michael@0 | 686 | |
michael@0 | 687 | /* Currently, the only difference is that internal representation |
michael@0 | 688 | * puts RANDOM_FLAG at bit 31 (Most-significant bit), but |
michael@0 | 689 | * public representation puts this bit at bit 28 |
michael@0 | 690 | */ |
michael@0 | 691 | unsigned long |
michael@0 | 692 | SECMOD_PubMechFlagstoInternal(unsigned long publicFlags) |
michael@0 | 693 | { |
michael@0 | 694 | unsigned long internalFlags = publicFlags; |
michael@0 | 695 | |
michael@0 | 696 | if (publicFlags & PUBLIC_MECH_RANDOM_FLAG) { |
michael@0 | 697 | internalFlags &= ~PUBLIC_MECH_RANDOM_FLAG; |
michael@0 | 698 | internalFlags |= SECMOD_RANDOM_FLAG; |
michael@0 | 699 | } |
michael@0 | 700 | return internalFlags; |
michael@0 | 701 | } |
michael@0 | 702 | |
michael@0 | 703 | unsigned long |
michael@0 | 704 | SECMOD_InternaltoPubMechFlags(unsigned long internalFlags) |
michael@0 | 705 | { |
michael@0 | 706 | unsigned long publicFlags = internalFlags; |
michael@0 | 707 | |
michael@0 | 708 | if (internalFlags & SECMOD_RANDOM_FLAG) { |
michael@0 | 709 | publicFlags &= ~SECMOD_RANDOM_FLAG; |
michael@0 | 710 | publicFlags |= PUBLIC_MECH_RANDOM_FLAG; |
michael@0 | 711 | } |
michael@0 | 712 | return publicFlags; |
michael@0 | 713 | } |
michael@0 | 714 | |
michael@0 | 715 | |
michael@0 | 716 | /* Public & Internal(Security Library) representation of */ |
michael@0 | 717 | /* cipher flags conversion */ |
michael@0 | 718 | /* Note: currently they are just stubs */ |
michael@0 | 719 | unsigned long |
michael@0 | 720 | SECMOD_PubCipherFlagstoInternal(unsigned long publicFlags) |
michael@0 | 721 | { |
michael@0 | 722 | return publicFlags; |
michael@0 | 723 | } |
michael@0 | 724 | |
michael@0 | 725 | unsigned long |
michael@0 | 726 | SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags) |
michael@0 | 727 | { |
michael@0 | 728 | return internalFlags; |
michael@0 | 729 | } |
michael@0 | 730 | |
michael@0 | 731 | /* Funtion reports true if module of modType is installed/configured */ |
michael@0 | 732 | PRBool |
michael@0 | 733 | SECMOD_IsModulePresent( unsigned long int pubCipherEnableFlags ) |
michael@0 | 734 | { |
michael@0 | 735 | PRBool result = PR_FALSE; |
michael@0 | 736 | SECMODModuleList *mods; |
michael@0 | 737 | |
michael@0 | 738 | if (!moduleLock) { |
michael@0 | 739 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 740 | return result; |
michael@0 | 741 | } |
michael@0 | 742 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 743 | mods = SECMOD_GetDefaultModuleList(); |
michael@0 | 744 | for ( ; mods != NULL; mods = mods->next) { |
michael@0 | 745 | if (mods->module->ssl[0] & |
michael@0 | 746 | SECMOD_PubCipherFlagstoInternal(pubCipherEnableFlags)) { |
michael@0 | 747 | result = PR_TRUE; |
michael@0 | 748 | } |
michael@0 | 749 | } |
michael@0 | 750 | |
michael@0 | 751 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 752 | return result; |
michael@0 | 753 | } |
michael@0 | 754 | |
michael@0 | 755 | /* create a new ModuleListElement */ |
michael@0 | 756 | SECMODModuleList *SECMOD_NewModuleListElement(void) |
michael@0 | 757 | { |
michael@0 | 758 | SECMODModuleList *newModList; |
michael@0 | 759 | |
michael@0 | 760 | newModList= (SECMODModuleList *) PORT_Alloc(sizeof(SECMODModuleList)); |
michael@0 | 761 | if (newModList) { |
michael@0 | 762 | newModList->next = NULL; |
michael@0 | 763 | newModList->module = NULL; |
michael@0 | 764 | } |
michael@0 | 765 | return newModList; |
michael@0 | 766 | } |
michael@0 | 767 | |
michael@0 | 768 | /* |
michael@0 | 769 | * make a new reference to a module so It doesn't go away on us |
michael@0 | 770 | */ |
michael@0 | 771 | SECMODModule * |
michael@0 | 772 | SECMOD_ReferenceModule(SECMODModule *module) |
michael@0 | 773 | { |
michael@0 | 774 | PZ_Lock(module->refLock); |
michael@0 | 775 | PORT_Assert(module->refCount > 0); |
michael@0 | 776 | |
michael@0 | 777 | module->refCount++; |
michael@0 | 778 | PZ_Unlock(module->refLock); |
michael@0 | 779 | return module; |
michael@0 | 780 | } |
michael@0 | 781 | |
michael@0 | 782 | |
michael@0 | 783 | /* destroy an existing module */ |
michael@0 | 784 | void |
michael@0 | 785 | SECMOD_DestroyModule(SECMODModule *module) |
michael@0 | 786 | { |
michael@0 | 787 | PRBool willfree = PR_FALSE; |
michael@0 | 788 | int slotCount; |
michael@0 | 789 | int i; |
michael@0 | 790 | |
michael@0 | 791 | PZ_Lock(module->refLock); |
michael@0 | 792 | if (module->refCount-- == 1) { |
michael@0 | 793 | willfree = PR_TRUE; |
michael@0 | 794 | } |
michael@0 | 795 | PORT_Assert(willfree || (module->refCount > 0)); |
michael@0 | 796 | PZ_Unlock(module->refLock); |
michael@0 | 797 | |
michael@0 | 798 | if (!willfree) { |
michael@0 | 799 | return; |
michael@0 | 800 | } |
michael@0 | 801 | |
michael@0 | 802 | if (module->parent != NULL) { |
michael@0 | 803 | SECMODModule *parent = module->parent; |
michael@0 | 804 | /* paranoia, don't loop forever if the modules are looped */ |
michael@0 | 805 | module->parent = NULL; |
michael@0 | 806 | SECMOD_DestroyModule(parent); |
michael@0 | 807 | } |
michael@0 | 808 | |
michael@0 | 809 | /* slots can't really disappear until our module starts freeing them, |
michael@0 | 810 | * so this check is safe */ |
michael@0 | 811 | slotCount = module->slotCount; |
michael@0 | 812 | if (slotCount == 0) { |
michael@0 | 813 | SECMOD_SlotDestroyModule(module,PR_FALSE); |
michael@0 | 814 | return; |
michael@0 | 815 | } |
michael@0 | 816 | |
michael@0 | 817 | /* now free all out slots, when they are done, they will cause the |
michael@0 | 818 | * module to disappear altogether */ |
michael@0 | 819 | for (i=0 ; i < slotCount; i++) { |
michael@0 | 820 | if (!module->slots[i]->disabled) { |
michael@0 | 821 | PK11_ClearSlotList(module->slots[i]); |
michael@0 | 822 | } |
michael@0 | 823 | PK11_FreeSlot(module->slots[i]); |
michael@0 | 824 | } |
michael@0 | 825 | /* WARNING: once the last slot has been freed is it possible (even likely) |
michael@0 | 826 | * that module is no more... touching it now is a good way to go south */ |
michael@0 | 827 | } |
michael@0 | 828 | |
michael@0 | 829 | |
michael@0 | 830 | /* we can only get here if we've destroyed the module, or some one has |
michael@0 | 831 | * erroneously freed a slot that wasn't referenced. */ |
michael@0 | 832 | void |
michael@0 | 833 | SECMOD_SlotDestroyModule(SECMODModule *module, PRBool fromSlot) |
michael@0 | 834 | { |
michael@0 | 835 | PRBool willfree = PR_FALSE; |
michael@0 | 836 | if (fromSlot) { |
michael@0 | 837 | PORT_Assert(module->refCount == 0); |
michael@0 | 838 | PZ_Lock(module->refLock); |
michael@0 | 839 | if (module->slotCount-- == 1) { |
michael@0 | 840 | willfree = PR_TRUE; |
michael@0 | 841 | } |
michael@0 | 842 | PORT_Assert(willfree || (module->slotCount > 0)); |
michael@0 | 843 | PZ_Unlock(module->refLock); |
michael@0 | 844 | if (!willfree) return; |
michael@0 | 845 | } |
michael@0 | 846 | |
michael@0 | 847 | if (module == pendingModule) { |
michael@0 | 848 | pendingModule = NULL; |
michael@0 | 849 | } |
michael@0 | 850 | |
michael@0 | 851 | if (module->loaded) { |
michael@0 | 852 | SECMOD_UnloadModule(module); |
michael@0 | 853 | } |
michael@0 | 854 | PZ_DestroyLock(module->refLock); |
michael@0 | 855 | PORT_FreeArena(module->arena,PR_FALSE); |
michael@0 | 856 | secmod_PrivateModuleCount--; |
michael@0 | 857 | } |
michael@0 | 858 | |
michael@0 | 859 | /* destroy a list element |
michael@0 | 860 | * this destroys a single element, and returns the next element |
michael@0 | 861 | * on the chain. It makes it easy to implement for loops to delete |
michael@0 | 862 | * the chain. It also make deleting a single element easy */ |
michael@0 | 863 | SECMODModuleList * |
michael@0 | 864 | SECMOD_DestroyModuleListElement(SECMODModuleList *element) |
michael@0 | 865 | { |
michael@0 | 866 | SECMODModuleList *next = element->next; |
michael@0 | 867 | |
michael@0 | 868 | if (element->module) { |
michael@0 | 869 | SECMOD_DestroyModule(element->module); |
michael@0 | 870 | element->module = NULL; |
michael@0 | 871 | } |
michael@0 | 872 | PORT_Free(element); |
michael@0 | 873 | return next; |
michael@0 | 874 | } |
michael@0 | 875 | |
michael@0 | 876 | |
michael@0 | 877 | /* |
michael@0 | 878 | * Destroy an entire module list |
michael@0 | 879 | */ |
michael@0 | 880 | void |
michael@0 | 881 | SECMOD_DestroyModuleList(SECMODModuleList *list) |
michael@0 | 882 | { |
michael@0 | 883 | SECMODModuleList *lp; |
michael@0 | 884 | |
michael@0 | 885 | for ( lp = list; lp != NULL; lp = SECMOD_DestroyModuleListElement(lp)) ; |
michael@0 | 886 | } |
michael@0 | 887 | |
michael@0 | 888 | PRBool |
michael@0 | 889 | SECMOD_CanDeleteInternalModule(void) |
michael@0 | 890 | { |
michael@0 | 891 | return (PRBool) (pendingModule == NULL); |
michael@0 | 892 | } |
michael@0 | 893 | |
michael@0 | 894 | /* |
michael@0 | 895 | * check to see if the module has added new slots. PKCS 11 v2.20 allows for |
michael@0 | 896 | * modules to add new slots, but never remove them. Slots cannot be added |
michael@0 | 897 | * between a call to C_GetSlotLlist(Flag, NULL, &count) and the subsequent |
michael@0 | 898 | * C_GetSlotList(flag, &data, &count) so that the array doesn't accidently |
michael@0 | 899 | * grow on the caller. It is permissible for the slots to increase between |
michael@0 | 900 | * successive calls with NULL to get the size. |
michael@0 | 901 | */ |
michael@0 | 902 | SECStatus |
michael@0 | 903 | SECMOD_UpdateSlotList(SECMODModule *mod) |
michael@0 | 904 | { |
michael@0 | 905 | CK_RV crv; |
michael@0 | 906 | CK_ULONG count; |
michael@0 | 907 | CK_ULONG i, oldCount; |
michael@0 | 908 | PRBool freeRef = PR_FALSE; |
michael@0 | 909 | void *mark = NULL; |
michael@0 | 910 | CK_ULONG *slotIDs = NULL; |
michael@0 | 911 | PK11SlotInfo **newSlots = NULL; |
michael@0 | 912 | PK11SlotInfo **oldSlots = NULL; |
michael@0 | 913 | |
michael@0 | 914 | if (!moduleLock) { |
michael@0 | 915 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 916 | return SECFailure; |
michael@0 | 917 | } |
michael@0 | 918 | |
michael@0 | 919 | /* C_GetSlotList is not a session function, make sure |
michael@0 | 920 | * calls are serialized */ |
michael@0 | 921 | PZ_Lock(mod->refLock); |
michael@0 | 922 | freeRef = PR_TRUE; |
michael@0 | 923 | /* see if the number of slots have changed */ |
michael@0 | 924 | crv = PK11_GETTAB(mod)->C_GetSlotList(PR_FALSE, NULL, &count); |
michael@0 | 925 | if (crv != CKR_OK) { |
michael@0 | 926 | PORT_SetError(PK11_MapError(crv)); |
michael@0 | 927 | goto loser; |
michael@0 | 928 | } |
michael@0 | 929 | /* nothing new, blow out early, we want this function to be quick |
michael@0 | 930 | * and cheap in the normal case */ |
michael@0 | 931 | if (count == mod->slotCount) { |
michael@0 | 932 | PZ_Unlock(mod->refLock); |
michael@0 | 933 | return SECSuccess; |
michael@0 | 934 | } |
michael@0 | 935 | if (count < (CK_ULONG)mod->slotCount) { |
michael@0 | 936 | /* shouldn't happen with a properly functioning PKCS #11 module */ |
michael@0 | 937 | PORT_SetError( SEC_ERROR_INCOMPATIBLE_PKCS11 ); |
michael@0 | 938 | goto loser; |
michael@0 | 939 | } |
michael@0 | 940 | |
michael@0 | 941 | /* get the new slot list */ |
michael@0 | 942 | slotIDs = PORT_NewArray(CK_SLOT_ID, count); |
michael@0 | 943 | if (slotIDs == NULL) { |
michael@0 | 944 | goto loser; |
michael@0 | 945 | } |
michael@0 | 946 | |
michael@0 | 947 | crv = PK11_GETTAB(mod)->C_GetSlotList(PR_FALSE, slotIDs, &count); |
michael@0 | 948 | if (crv != CKR_OK) { |
michael@0 | 949 | PORT_SetError(PK11_MapError(crv)); |
michael@0 | 950 | goto loser; |
michael@0 | 951 | } |
michael@0 | 952 | freeRef = PR_FALSE; |
michael@0 | 953 | PZ_Unlock(mod->refLock); |
michael@0 | 954 | mark = PORT_ArenaMark(mod->arena); |
michael@0 | 955 | if (mark == NULL) { |
michael@0 | 956 | goto loser; |
michael@0 | 957 | } |
michael@0 | 958 | newSlots = PORT_ArenaZNewArray(mod->arena,PK11SlotInfo *,count); |
michael@0 | 959 | |
michael@0 | 960 | /* walk down the new slot ID list returned from the module. We keep |
michael@0 | 961 | * the old slots which match a returned ID, and we initialize the new |
michael@0 | 962 | * slots. */ |
michael@0 | 963 | for (i=0; i < count; i++) { |
michael@0 | 964 | PK11SlotInfo *slot = SECMOD_FindSlotByID(mod,slotIDs[i]); |
michael@0 | 965 | |
michael@0 | 966 | if (!slot) { |
michael@0 | 967 | /* we have a new slot create a new slot data structure */ |
michael@0 | 968 | slot = PK11_NewSlotInfo(mod); |
michael@0 | 969 | if (!slot) { |
michael@0 | 970 | goto loser; |
michael@0 | 971 | } |
michael@0 | 972 | PK11_InitSlot(mod, slotIDs[i], slot); |
michael@0 | 973 | STAN_InitTokenForSlotInfo(NULL, slot); |
michael@0 | 974 | } |
michael@0 | 975 | newSlots[i] = slot; |
michael@0 | 976 | } |
michael@0 | 977 | STAN_ResetTokenInterator(NULL); |
michael@0 | 978 | PORT_Free(slotIDs); |
michael@0 | 979 | slotIDs = NULL; |
michael@0 | 980 | PORT_ArenaUnmark(mod->arena, mark); |
michael@0 | 981 | |
michael@0 | 982 | /* until this point we're still using the old slot list. Now we update |
michael@0 | 983 | * module slot list. We update the slots (array) first then the count, |
michael@0 | 984 | * since we've already guarrenteed that count has increased (just in case |
michael@0 | 985 | * someone is looking at the slots field of module without holding the |
michael@0 | 986 | * moduleLock */ |
michael@0 | 987 | SECMOD_GetWriteLock(moduleLock); |
michael@0 | 988 | oldCount =mod->slotCount; |
michael@0 | 989 | oldSlots = mod->slots; |
michael@0 | 990 | mod->slots = newSlots; /* typical arena 'leak'... old mod->slots is |
michael@0 | 991 | * allocated out of the module arena and won't |
michael@0 | 992 | * be freed until the module is freed */ |
michael@0 | 993 | mod->slotCount = count; |
michael@0 | 994 | SECMOD_ReleaseWriteLock(moduleLock); |
michael@0 | 995 | /* free our old references before forgetting about oldSlot*/ |
michael@0 | 996 | for (i=0; i < oldCount; i++) { |
michael@0 | 997 | PK11_FreeSlot(oldSlots[i]); |
michael@0 | 998 | } |
michael@0 | 999 | return SECSuccess; |
michael@0 | 1000 | |
michael@0 | 1001 | loser: |
michael@0 | 1002 | if (freeRef) { |
michael@0 | 1003 | PZ_Unlock(mod->refLock); |
michael@0 | 1004 | } |
michael@0 | 1005 | if (slotIDs) { |
michael@0 | 1006 | PORT_Free(slotIDs); |
michael@0 | 1007 | } |
michael@0 | 1008 | /* free all the slots we allocated. newSlots are part of the |
michael@0 | 1009 | * mod arena. NOTE: the newSlots array contain both new and old |
michael@0 | 1010 | * slots, but we kept a reference to the old slots when we built the new |
michael@0 | 1011 | * array, so we need to free all the slots in newSlots array. */ |
michael@0 | 1012 | if (newSlots) { |
michael@0 | 1013 | for (i=0; i < count; i++) { |
michael@0 | 1014 | if (newSlots[i] == NULL) { |
michael@0 | 1015 | break; /* hit the last one */ |
michael@0 | 1016 | } |
michael@0 | 1017 | PK11_FreeSlot(newSlots[i]); |
michael@0 | 1018 | } |
michael@0 | 1019 | } |
michael@0 | 1020 | /* must come after freeing newSlots */ |
michael@0 | 1021 | if (mark) { |
michael@0 | 1022 | PORT_ArenaRelease(mod->arena, mark); |
michael@0 | 1023 | } |
michael@0 | 1024 | return SECFailure; |
michael@0 | 1025 | } |
michael@0 | 1026 | |
michael@0 | 1027 | /* |
michael@0 | 1028 | * this handles modules that do not support C_WaitForSlotEvent(). |
michael@0 | 1029 | * The internal flags are stored. Note that C_WaitForSlotEvent() does not |
michael@0 | 1030 | * have a timeout, so we don't have one for handleWaitForSlotEvent() either. |
michael@0 | 1031 | */ |
michael@0 | 1032 | PK11SlotInfo * |
michael@0 | 1033 | secmod_HandleWaitForSlotEvent(SECMODModule *mod, unsigned long flags, |
michael@0 | 1034 | PRIntervalTime latency) |
michael@0 | 1035 | { |
michael@0 | 1036 | PRBool removableSlotsFound = PR_FALSE; |
michael@0 | 1037 | int i; |
michael@0 | 1038 | int error = SEC_ERROR_NO_EVENT; |
michael@0 | 1039 | |
michael@0 | 1040 | if (!moduleLock) { |
michael@0 | 1041 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 1042 | return NULL; |
michael@0 | 1043 | } |
michael@0 | 1044 | PZ_Lock(mod->refLock); |
michael@0 | 1045 | if (mod->evControlMask & SECMOD_END_WAIT) { |
michael@0 | 1046 | mod->evControlMask &= ~SECMOD_END_WAIT; |
michael@0 | 1047 | PZ_Unlock(mod->refLock); |
michael@0 | 1048 | PORT_SetError(SEC_ERROR_NO_EVENT); |
michael@0 | 1049 | return NULL; |
michael@0 | 1050 | } |
michael@0 | 1051 | mod->evControlMask |= SECMOD_WAIT_SIMULATED_EVENT; |
michael@0 | 1052 | while (mod->evControlMask & SECMOD_WAIT_SIMULATED_EVENT) { |
michael@0 | 1053 | PZ_Unlock(mod->refLock); |
michael@0 | 1054 | /* now is a good time to see if new slots have been added */ |
michael@0 | 1055 | SECMOD_UpdateSlotList(mod); |
michael@0 | 1056 | |
michael@0 | 1057 | /* loop through all the slots on a module */ |
michael@0 | 1058 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 1059 | for (i=0; i < mod->slotCount; i++) { |
michael@0 | 1060 | PK11SlotInfo *slot = mod->slots[i]; |
michael@0 | 1061 | PRUint16 series; |
michael@0 | 1062 | PRBool present; |
michael@0 | 1063 | |
michael@0 | 1064 | /* perm modules do not change */ |
michael@0 | 1065 | if (slot->isPerm) { |
michael@0 | 1066 | continue; |
michael@0 | 1067 | } |
michael@0 | 1068 | removableSlotsFound = PR_TRUE; |
michael@0 | 1069 | /* simulate the PKCS #11 module flags. are the flags different |
michael@0 | 1070 | * from the last time we called? */ |
michael@0 | 1071 | series = slot->series; |
michael@0 | 1072 | present = PK11_IsPresent(slot); |
michael@0 | 1073 | if ((slot->flagSeries != series) || (slot->flagState != present)) { |
michael@0 | 1074 | slot->flagState = present; |
michael@0 | 1075 | slot->flagSeries = series; |
michael@0 | 1076 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 1077 | PZ_Lock(mod->refLock); |
michael@0 | 1078 | mod->evControlMask &= ~SECMOD_END_WAIT; |
michael@0 | 1079 | PZ_Unlock(mod->refLock); |
michael@0 | 1080 | return PK11_ReferenceSlot(slot); |
michael@0 | 1081 | } |
michael@0 | 1082 | } |
michael@0 | 1083 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 1084 | /* if everything was perm modules, don't lock up forever */ |
michael@0 | 1085 | if ((mod->slotCount !=0) && !removableSlotsFound) { |
michael@0 | 1086 | error =SEC_ERROR_NO_SLOT_SELECTED; |
michael@0 | 1087 | PZ_Lock(mod->refLock); |
michael@0 | 1088 | break; |
michael@0 | 1089 | } |
michael@0 | 1090 | if (flags & CKF_DONT_BLOCK) { |
michael@0 | 1091 | PZ_Lock(mod->refLock); |
michael@0 | 1092 | break; |
michael@0 | 1093 | } |
michael@0 | 1094 | PR_Sleep(latency); |
michael@0 | 1095 | PZ_Lock(mod->refLock); |
michael@0 | 1096 | } |
michael@0 | 1097 | mod->evControlMask &= ~SECMOD_END_WAIT; |
michael@0 | 1098 | PZ_Unlock(mod->refLock); |
michael@0 | 1099 | PORT_SetError(error); |
michael@0 | 1100 | return NULL; |
michael@0 | 1101 | } |
michael@0 | 1102 | |
michael@0 | 1103 | /* |
michael@0 | 1104 | * this function waits for a token event on any slot of a given module |
michael@0 | 1105 | * This function should not be called from more than one thread of the |
michael@0 | 1106 | * same process (though other threads can make other library calls |
michael@0 | 1107 | * on this module while this call is blocked). |
michael@0 | 1108 | */ |
michael@0 | 1109 | PK11SlotInfo * |
michael@0 | 1110 | SECMOD_WaitForAnyTokenEvent(SECMODModule *mod, unsigned long flags, |
michael@0 | 1111 | PRIntervalTime latency) |
michael@0 | 1112 | { |
michael@0 | 1113 | CK_SLOT_ID id; |
michael@0 | 1114 | CK_RV crv; |
michael@0 | 1115 | PK11SlotInfo *slot; |
michael@0 | 1116 | |
michael@0 | 1117 | if (!pk11_getFinalizeModulesOption() || |
michael@0 | 1118 | ((mod->cryptokiVersion.major == 2) && |
michael@0 | 1119 | (mod->cryptokiVersion.minor < 1))) { |
michael@0 | 1120 | /* if we are sharing the module with other software in our |
michael@0 | 1121 | * address space, we can't reliably use C_WaitForSlotEvent(), |
michael@0 | 1122 | * and if the module is version 2.0, C_WaitForSlotEvent() doesn't |
michael@0 | 1123 | * exist */ |
michael@0 | 1124 | return secmod_HandleWaitForSlotEvent(mod, flags, latency); |
michael@0 | 1125 | } |
michael@0 | 1126 | /* first the the PKCS #11 call */ |
michael@0 | 1127 | PZ_Lock(mod->refLock); |
michael@0 | 1128 | if (mod->evControlMask & SECMOD_END_WAIT) { |
michael@0 | 1129 | goto end_wait; |
michael@0 | 1130 | } |
michael@0 | 1131 | mod->evControlMask |= SECMOD_WAIT_PKCS11_EVENT; |
michael@0 | 1132 | PZ_Unlock(mod->refLock); |
michael@0 | 1133 | crv = PK11_GETTAB(mod)->C_WaitForSlotEvent(flags, &id, NULL); |
michael@0 | 1134 | PZ_Lock(mod->refLock); |
michael@0 | 1135 | mod->evControlMask &= ~SECMOD_WAIT_PKCS11_EVENT; |
michael@0 | 1136 | /* if we are in end wait, short circuit now, don't even risk |
michael@0 | 1137 | * going into secmod_HandleWaitForSlotEvent */ |
michael@0 | 1138 | if (mod->evControlMask & SECMOD_END_WAIT) { |
michael@0 | 1139 | goto end_wait; |
michael@0 | 1140 | } |
michael@0 | 1141 | PZ_Unlock(mod->refLock); |
michael@0 | 1142 | if (crv == CKR_FUNCTION_NOT_SUPPORTED) { |
michael@0 | 1143 | /* module doesn't support that call, simulate it */ |
michael@0 | 1144 | return secmod_HandleWaitForSlotEvent(mod, flags, latency); |
michael@0 | 1145 | } |
michael@0 | 1146 | if (crv != CKR_OK) { |
michael@0 | 1147 | /* we can get this error if finalize was called while we were |
michael@0 | 1148 | * still running. This is the only way to force a C_WaitForSlotEvent() |
michael@0 | 1149 | * to return in PKCS #11. In this case, just return that there |
michael@0 | 1150 | * was no event. */ |
michael@0 | 1151 | if (crv == CKR_CRYPTOKI_NOT_INITIALIZED) { |
michael@0 | 1152 | PORT_SetError(SEC_ERROR_NO_EVENT); |
michael@0 | 1153 | } else { |
michael@0 | 1154 | PORT_SetError(PK11_MapError(crv)); |
michael@0 | 1155 | } |
michael@0 | 1156 | return NULL; |
michael@0 | 1157 | } |
michael@0 | 1158 | slot = SECMOD_FindSlotByID(mod, id); |
michael@0 | 1159 | if (slot == NULL) { |
michael@0 | 1160 | /* possibly a new slot that was added? */ |
michael@0 | 1161 | SECMOD_UpdateSlotList(mod); |
michael@0 | 1162 | slot = SECMOD_FindSlotByID(mod, id); |
michael@0 | 1163 | } |
michael@0 | 1164 | /* if we are in the delay period for the "isPresent" call, reset |
michael@0 | 1165 | * the delay since we know things have probably changed... */ |
michael@0 | 1166 | if (slot && slot->nssToken && slot->nssToken->slot) { |
michael@0 | 1167 | nssSlot_ResetDelay(slot->nssToken->slot); |
michael@0 | 1168 | } |
michael@0 | 1169 | return slot; |
michael@0 | 1170 | |
michael@0 | 1171 | /* must be called with the lock on. */ |
michael@0 | 1172 | end_wait: |
michael@0 | 1173 | mod->evControlMask &= ~SECMOD_END_WAIT; |
michael@0 | 1174 | PZ_Unlock(mod->refLock); |
michael@0 | 1175 | PORT_SetError(SEC_ERROR_NO_EVENT); |
michael@0 | 1176 | return NULL; |
michael@0 | 1177 | } |
michael@0 | 1178 | |
michael@0 | 1179 | /* |
michael@0 | 1180 | * This function "wakes up" WaitForAnyTokenEvent. It's a pretty drastic |
michael@0 | 1181 | * function, possibly bringing down the pkcs #11 module in question. This |
michael@0 | 1182 | * should be OK because 1) it does reinitialize, and 2) it should only be |
michael@0 | 1183 | * called when we are on our way to tear the whole system down anyway. |
michael@0 | 1184 | */ |
michael@0 | 1185 | SECStatus |
michael@0 | 1186 | SECMOD_CancelWait(SECMODModule *mod) |
michael@0 | 1187 | { |
michael@0 | 1188 | unsigned long controlMask = mod->evControlMask; |
michael@0 | 1189 | SECStatus rv = SECSuccess; |
michael@0 | 1190 | CK_RV crv; |
michael@0 | 1191 | |
michael@0 | 1192 | PZ_Lock(mod->refLock); |
michael@0 | 1193 | mod->evControlMask |= SECMOD_END_WAIT; |
michael@0 | 1194 | controlMask = mod->evControlMask; |
michael@0 | 1195 | if (controlMask & SECMOD_WAIT_PKCS11_EVENT) { |
michael@0 | 1196 | if (!pk11_getFinalizeModulesOption()) { |
michael@0 | 1197 | /* can't get here unless pk11_getFinalizeModulesOption is set */ |
michael@0 | 1198 | PORT_Assert(0); |
michael@0 | 1199 | PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
michael@0 | 1200 | rv = SECFailure; |
michael@0 | 1201 | goto loser; |
michael@0 | 1202 | } |
michael@0 | 1203 | /* NOTE: this call will drop all transient keys, in progress |
michael@0 | 1204 | * operations, and any authentication. This is the only documented |
michael@0 | 1205 | * way to get WaitForSlotEvent to return. Also note: for non-thread |
michael@0 | 1206 | * safe tokens, we need to hold the module lock, this is not yet at |
michael@0 | 1207 | * system shutdown/startup time, so we need to protect these calls */ |
michael@0 | 1208 | crv = PK11_GETTAB(mod)->C_Finalize(NULL); |
michael@0 | 1209 | /* ok, we slammed the module down, now we need to reinit it in case |
michael@0 | 1210 | * we intend to use it again */ |
michael@0 | 1211 | if (CKR_OK == crv) { |
michael@0 | 1212 | PRBool alreadyLoaded; |
michael@0 | 1213 | secmod_ModuleInit(mod, NULL, &alreadyLoaded); |
michael@0 | 1214 | } else { |
michael@0 | 1215 | /* Finalized failed for some reason, notify the application |
michael@0 | 1216 | * so maybe it has a prayer of recovering... */ |
michael@0 | 1217 | PORT_SetError(PK11_MapError(crv)); |
michael@0 | 1218 | rv = SECFailure; |
michael@0 | 1219 | } |
michael@0 | 1220 | } else if (controlMask & SECMOD_WAIT_SIMULATED_EVENT) { |
michael@0 | 1221 | mod->evControlMask &= ~SECMOD_WAIT_SIMULATED_EVENT; |
michael@0 | 1222 | /* Simulated events will eventually timeout |
michael@0 | 1223 | * and wake up in the loop */ |
michael@0 | 1224 | } |
michael@0 | 1225 | loser: |
michael@0 | 1226 | PZ_Unlock(mod->refLock); |
michael@0 | 1227 | return rv; |
michael@0 | 1228 | } |
michael@0 | 1229 | |
michael@0 | 1230 | /* |
michael@0 | 1231 | * check to see if the module has removable slots that we may need to |
michael@0 | 1232 | * watch for. |
michael@0 | 1233 | */ |
michael@0 | 1234 | PRBool |
michael@0 | 1235 | SECMOD_HasRemovableSlots(SECMODModule *mod) |
michael@0 | 1236 | { |
michael@0 | 1237 | int i; |
michael@0 | 1238 | PRBool ret = PR_FALSE; |
michael@0 | 1239 | |
michael@0 | 1240 | if (!moduleLock) { |
michael@0 | 1241 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 1242 | return ret; |
michael@0 | 1243 | } |
michael@0 | 1244 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 1245 | for (i=0; i < mod->slotCount; i++) { |
michael@0 | 1246 | PK11SlotInfo *slot = mod->slots[i]; |
michael@0 | 1247 | /* perm modules are not inserted or removed */ |
michael@0 | 1248 | if (slot->isPerm) { |
michael@0 | 1249 | continue; |
michael@0 | 1250 | } |
michael@0 | 1251 | ret = PR_TRUE; |
michael@0 | 1252 | break; |
michael@0 | 1253 | } |
michael@0 | 1254 | if (mod->slotCount == 0 ) { |
michael@0 | 1255 | ret = PR_TRUE; |
michael@0 | 1256 | } |
michael@0 | 1257 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 1258 | return ret; |
michael@0 | 1259 | } |
michael@0 | 1260 | |
michael@0 | 1261 | /* |
michael@0 | 1262 | * helper function to actually create and destroy user defined slots |
michael@0 | 1263 | */ |
michael@0 | 1264 | static SECStatus |
michael@0 | 1265 | secmod_UserDBOp(PK11SlotInfo *slot, CK_OBJECT_CLASS objClass, |
michael@0 | 1266 | const char *sendSpec) |
michael@0 | 1267 | { |
michael@0 | 1268 | CK_OBJECT_HANDLE dummy; |
michael@0 | 1269 | CK_ATTRIBUTE template[2] ; |
michael@0 | 1270 | CK_ATTRIBUTE *attrs = template; |
michael@0 | 1271 | CK_RV crv; |
michael@0 | 1272 | |
michael@0 | 1273 | PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass)); attrs++; |
michael@0 | 1274 | PK11_SETATTRS(attrs, CKA_NETSCAPE_MODULE_SPEC , (unsigned char *)sendSpec, |
michael@0 | 1275 | strlen(sendSpec)+1); attrs++; |
michael@0 | 1276 | |
michael@0 | 1277 | PORT_Assert(attrs-template <= 2); |
michael@0 | 1278 | |
michael@0 | 1279 | |
michael@0 | 1280 | PK11_EnterSlotMonitor(slot); |
michael@0 | 1281 | crv = PK11_CreateNewObject(slot, slot->session, |
michael@0 | 1282 | template, attrs-template, PR_FALSE, &dummy); |
michael@0 | 1283 | PK11_ExitSlotMonitor(slot); |
michael@0 | 1284 | |
michael@0 | 1285 | if (crv != CKR_OK) { |
michael@0 | 1286 | PORT_SetError(PK11_MapError(crv)); |
michael@0 | 1287 | return SECFailure; |
michael@0 | 1288 | } |
michael@0 | 1289 | return SECMOD_UpdateSlotList(slot->module); |
michael@0 | 1290 | } |
michael@0 | 1291 | |
michael@0 | 1292 | /* |
michael@0 | 1293 | * return true if the selected slot ID is not present or doesn't exist |
michael@0 | 1294 | */ |
michael@0 | 1295 | static PRBool |
michael@0 | 1296 | secmod_SlotIsEmpty(SECMODModule *mod, CK_SLOT_ID slotID) |
michael@0 | 1297 | { |
michael@0 | 1298 | PK11SlotInfo *slot = SECMOD_LookupSlot(mod->moduleID, slotID); |
michael@0 | 1299 | if (slot) { |
michael@0 | 1300 | PRBool present = PK11_IsPresent(slot); |
michael@0 | 1301 | PK11_FreeSlot(slot); |
michael@0 | 1302 | if (present) { |
michael@0 | 1303 | return PR_FALSE; |
michael@0 | 1304 | } |
michael@0 | 1305 | } |
michael@0 | 1306 | /* it doesn't exist or isn't present, it's available */ |
michael@0 | 1307 | return PR_TRUE; |
michael@0 | 1308 | } |
michael@0 | 1309 | |
michael@0 | 1310 | /* |
michael@0 | 1311 | * Find an unused slot id in module. |
michael@0 | 1312 | */ |
michael@0 | 1313 | static CK_SLOT_ID |
michael@0 | 1314 | secmod_FindFreeSlot(SECMODModule *mod) |
michael@0 | 1315 | { |
michael@0 | 1316 | CK_SLOT_ID i, minSlotID, maxSlotID; |
michael@0 | 1317 | |
michael@0 | 1318 | /* look for a free slot id on the internal module */ |
michael@0 | 1319 | if (mod->internal && mod->isFIPS) { |
michael@0 | 1320 | minSlotID = SFTK_MIN_FIPS_USER_SLOT_ID; |
michael@0 | 1321 | maxSlotID = SFTK_MAX_FIPS_USER_SLOT_ID; |
michael@0 | 1322 | } else { |
michael@0 | 1323 | minSlotID = SFTK_MIN_USER_SLOT_ID; |
michael@0 | 1324 | maxSlotID = SFTK_MAX_USER_SLOT_ID; |
michael@0 | 1325 | } |
michael@0 | 1326 | for (i=minSlotID; i < maxSlotID; i++) { |
michael@0 | 1327 | if (secmod_SlotIsEmpty(mod,i)) { |
michael@0 | 1328 | return i; |
michael@0 | 1329 | } |
michael@0 | 1330 | } |
michael@0 | 1331 | PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED); |
michael@0 | 1332 | return (CK_SLOT_ID) -1; |
michael@0 | 1333 | } |
michael@0 | 1334 | |
michael@0 | 1335 | /* |
michael@0 | 1336 | * Attempt to open a new slot. |
michael@0 | 1337 | * |
michael@0 | 1338 | * This works the same os OpenUserDB except it can be called against |
michael@0 | 1339 | * any module that understands the softoken protocol for opening new |
michael@0 | 1340 | * slots, not just the softoken itself. If the selected module does not |
michael@0 | 1341 | * understand the protocol, C_CreateObject will fail with |
michael@0 | 1342 | * CKR_INVALID_ATTRIBUTE, and SECMOD_OpenNewSlot will return NULL and set |
michael@0 | 1343 | * SEC_ERROR_BAD_DATA. |
michael@0 | 1344 | * |
michael@0 | 1345 | * NewSlots can be closed with SECMOD_CloseUserDB(); |
michael@0 | 1346 | * |
michael@0 | 1347 | * Modulespec is module dependent. |
michael@0 | 1348 | */ |
michael@0 | 1349 | PK11SlotInfo * |
michael@0 | 1350 | SECMOD_OpenNewSlot(SECMODModule *mod, const char *moduleSpec) |
michael@0 | 1351 | { |
michael@0 | 1352 | CK_SLOT_ID slotID = 0; |
michael@0 | 1353 | PK11SlotInfo *slot; |
michael@0 | 1354 | char *escSpec; |
michael@0 | 1355 | char *sendSpec; |
michael@0 | 1356 | SECStatus rv; |
michael@0 | 1357 | |
michael@0 | 1358 | slotID = secmod_FindFreeSlot(mod); |
michael@0 | 1359 | if (slotID == (CK_SLOT_ID) -1) { |
michael@0 | 1360 | return NULL; |
michael@0 | 1361 | } |
michael@0 | 1362 | |
michael@0 | 1363 | if (mod->slotCount == 0) { |
michael@0 | 1364 | return NULL; |
michael@0 | 1365 | } |
michael@0 | 1366 | |
michael@0 | 1367 | /* just grab the first slot in the module, any present slot should work */ |
michael@0 | 1368 | slot = PK11_ReferenceSlot(mod->slots[0]); |
michael@0 | 1369 | if (slot == NULL) { |
michael@0 | 1370 | return NULL; |
michael@0 | 1371 | } |
michael@0 | 1372 | |
michael@0 | 1373 | /* we've found the slot, now build the moduleSpec */ |
michael@0 | 1374 | escSpec = NSSUTIL_DoubleEscape(moduleSpec, '>', ']'); |
michael@0 | 1375 | if (escSpec == NULL) { |
michael@0 | 1376 | PK11_FreeSlot(slot); |
michael@0 | 1377 | return NULL; |
michael@0 | 1378 | } |
michael@0 | 1379 | sendSpec = PR_smprintf("tokens=[0x%x=<%s>]", slotID, escSpec); |
michael@0 | 1380 | PORT_Free(escSpec); |
michael@0 | 1381 | |
michael@0 | 1382 | if (sendSpec == NULL) { |
michael@0 | 1383 | /* PR_smprintf does not set SEC_ERROR_NO_MEMORY on failure. */ |
michael@0 | 1384 | PK11_FreeSlot(slot); |
michael@0 | 1385 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 1386 | return NULL; |
michael@0 | 1387 | } |
michael@0 | 1388 | rv = secmod_UserDBOp(slot, CKO_NETSCAPE_NEWSLOT, sendSpec); |
michael@0 | 1389 | PR_smprintf_free(sendSpec); |
michael@0 | 1390 | PK11_FreeSlot(slot); |
michael@0 | 1391 | if (rv != SECSuccess) { |
michael@0 | 1392 | return NULL; |
michael@0 | 1393 | } |
michael@0 | 1394 | |
michael@0 | 1395 | slot = SECMOD_FindSlotByID(mod, slotID); |
michael@0 | 1396 | if (slot) { |
michael@0 | 1397 | /* if we are in the delay period for the "isPresent" call, reset |
michael@0 | 1398 | * the delay since we know things have probably changed... */ |
michael@0 | 1399 | if (slot->nssToken && slot->nssToken->slot) { |
michael@0 | 1400 | nssSlot_ResetDelay(slot->nssToken->slot); |
michael@0 | 1401 | } |
michael@0 | 1402 | /* force the slot info structures to properly reset */ |
michael@0 | 1403 | (void)PK11_IsPresent(slot); |
michael@0 | 1404 | } |
michael@0 | 1405 | return slot; |
michael@0 | 1406 | } |
michael@0 | 1407 | |
michael@0 | 1408 | /* |
michael@0 | 1409 | * Open a new database using the softoken. The caller is responsible for making |
michael@0 | 1410 | * sure the module spec is correct and usable. The caller should ask for one |
michael@0 | 1411 | * new database per call if the caller wants to get meaningful information |
michael@0 | 1412 | * about the new database. |
michael@0 | 1413 | * |
michael@0 | 1414 | * moduleSpec is the same data that you would pass to softoken at |
michael@0 | 1415 | * initialization time under the 'tokens' options. For example, if you were |
michael@0 | 1416 | * to specify tokens=<0x4=[configdir='./mybackup' tokenDescription='Backup']> |
michael@0 | 1417 | * You would specify "configdir='./mybackup' tokenDescription='Backup'" as your |
michael@0 | 1418 | * module spec here. The slot ID will be calculated for you by |
michael@0 | 1419 | * SECMOD_OpenUserDB(). |
michael@0 | 1420 | * |
michael@0 | 1421 | * Typical parameters here are configdir, tokenDescription and flags. |
michael@0 | 1422 | * |
michael@0 | 1423 | * a Full list is below: |
michael@0 | 1424 | * |
michael@0 | 1425 | * |
michael@0 | 1426 | * configDir - The location of the databases for this token. If configDir is |
michael@0 | 1427 | * not specified, and noCertDB and noKeyDB is not specified, the load |
michael@0 | 1428 | * will fail. |
michael@0 | 1429 | * certPrefix - Cert prefix for this token. |
michael@0 | 1430 | * keyPrefix - Prefix for the key database for this token. (if not specified, |
michael@0 | 1431 | * certPrefix will be used). |
michael@0 | 1432 | * tokenDescription - The label value for this token returned in the |
michael@0 | 1433 | * CK_TOKEN_INFO structure with an internationalize string (UTF8). |
michael@0 | 1434 | * This value will be truncated at 32 bytes (no NULL, partial UTF8 |
michael@0 | 1435 | * characters dropped). You should specify a user friendly name here |
michael@0 | 1436 | * as this is the value the token will be referred to in most |
michael@0 | 1437 | * application UI's. You should make sure tokenDescription is unique. |
michael@0 | 1438 | * slotDescription - The slotDescription value for this token returned |
michael@0 | 1439 | * in the CK_SLOT_INFO structure with an internationalize string |
michael@0 | 1440 | * (UTF8). This value will be truncated at 64 bytes (no NULL, partial |
michael@0 | 1441 | * UTF8 characters dropped). This name will not change after the |
michael@0 | 1442 | * database is closed. It should have some number to make this unique. |
michael@0 | 1443 | * minPWLen - minimum password length for this token. |
michael@0 | 1444 | * flags - comma separated list of flag values, parsed case-insensitive. |
michael@0 | 1445 | * Valid flags are: |
michael@0 | 1446 | * readOnly - Databases should be opened read only. |
michael@0 | 1447 | * noCertDB - Don't try to open a certificate database. |
michael@0 | 1448 | * noKeyDB - Don't try to open a key database. |
michael@0 | 1449 | * forceOpen - Don't fail to initialize the token if the |
michael@0 | 1450 | * databases could not be opened. |
michael@0 | 1451 | * passwordRequired - zero length passwords are not acceptable |
michael@0 | 1452 | * (valid only if there is a keyDB). |
michael@0 | 1453 | * optimizeSpace - allocate smaller hash tables and lock tables. |
michael@0 | 1454 | * When this flag is not specified, Softoken will allocate |
michael@0 | 1455 | * large tables to prevent lock contention. |
michael@0 | 1456 | */ |
michael@0 | 1457 | PK11SlotInfo * |
michael@0 | 1458 | SECMOD_OpenUserDB(const char *moduleSpec) |
michael@0 | 1459 | { |
michael@0 | 1460 | SECMODModule *mod; |
michael@0 | 1461 | |
michael@0 | 1462 | if (moduleSpec == NULL) { |
michael@0 | 1463 | return NULL; |
michael@0 | 1464 | } |
michael@0 | 1465 | |
michael@0 | 1466 | /* NOTE: unlike most PK11 function, this does not return a reference |
michael@0 | 1467 | * to the module */ |
michael@0 | 1468 | mod = SECMOD_GetInternalModule(); |
michael@0 | 1469 | if (!mod) { |
michael@0 | 1470 | /* shouldn't happen */ |
michael@0 | 1471 | PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
michael@0 | 1472 | return NULL; |
michael@0 | 1473 | } |
michael@0 | 1474 | return SECMOD_OpenNewSlot(mod, moduleSpec); |
michael@0 | 1475 | } |
michael@0 | 1476 | |
michael@0 | 1477 | |
michael@0 | 1478 | /* |
michael@0 | 1479 | * close an already opened user database. NOTE: the database must be |
michael@0 | 1480 | * in the internal token, and must be one created with SECMOD_OpenUserDB(). |
michael@0 | 1481 | * Once the database is closed, the slot will remain as an empty slot |
michael@0 | 1482 | * until it's used again with SECMOD_OpenUserDB() or SECMOD_OpenNewSlot(). |
michael@0 | 1483 | */ |
michael@0 | 1484 | SECStatus |
michael@0 | 1485 | SECMOD_CloseUserDB(PK11SlotInfo *slot) |
michael@0 | 1486 | { |
michael@0 | 1487 | SECStatus rv; |
michael@0 | 1488 | char *sendSpec; |
michael@0 | 1489 | |
michael@0 | 1490 | sendSpec = PR_smprintf("tokens=[0x%x=<>]", slot->slotID); |
michael@0 | 1491 | if (sendSpec == NULL) { |
michael@0 | 1492 | /* PR_smprintf does not set no memory error */ |
michael@0 | 1493 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 1494 | return SECFailure; |
michael@0 | 1495 | } |
michael@0 | 1496 | rv = secmod_UserDBOp(slot, CKO_NETSCAPE_DELSLOT, sendSpec); |
michael@0 | 1497 | PR_smprintf_free(sendSpec); |
michael@0 | 1498 | /* if we are in the delay period for the "isPresent" call, reset |
michael@0 | 1499 | * the delay since we know things have probably changed... */ |
michael@0 | 1500 | if (slot->nssToken && slot->nssToken->slot) { |
michael@0 | 1501 | nssSlot_ResetDelay(slot->nssToken->slot); |
michael@0 | 1502 | /* force the slot info structures to properly reset */ |
michael@0 | 1503 | (void)PK11_IsPresent(slot); |
michael@0 | 1504 | } |
michael@0 | 1505 | return rv; |
michael@0 | 1506 | } |
michael@0 | 1507 | |
michael@0 | 1508 | /* |
michael@0 | 1509 | * Restart PKCS #11 modules after a fork(). See secmod.h for more information. |
michael@0 | 1510 | */ |
michael@0 | 1511 | SECStatus |
michael@0 | 1512 | SECMOD_RestartModules(PRBool force) |
michael@0 | 1513 | { |
michael@0 | 1514 | SECMODModuleList *mlp; |
michael@0 | 1515 | SECStatus rrv = SECSuccess; |
michael@0 | 1516 | int lastError = 0; |
michael@0 | 1517 | |
michael@0 | 1518 | if (!moduleLock) { |
michael@0 | 1519 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 1520 | return SECFailure; |
michael@0 | 1521 | } |
michael@0 | 1522 | |
michael@0 | 1523 | /* Only need to restart the PKCS #11 modules that were initialized */ |
michael@0 | 1524 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 1525 | for (mlp = modules; mlp != NULL; mlp = mlp->next) { |
michael@0 | 1526 | SECMODModule *mod = mlp->module; |
michael@0 | 1527 | CK_ULONG count; |
michael@0 | 1528 | SECStatus rv; |
michael@0 | 1529 | int i; |
michael@0 | 1530 | |
michael@0 | 1531 | /* If the module needs to be reset, do so */ |
michael@0 | 1532 | if (force || (PK11_GETTAB(mod)-> |
michael@0 | 1533 | C_GetSlotList(CK_FALSE, NULL, &count) != CKR_OK)) { |
michael@0 | 1534 | PRBool alreadyLoaded; |
michael@0 | 1535 | /* first call Finalize. This is not required by PKCS #11, but some |
michael@0 | 1536 | * older modules require it, and it doesn't hurt (compliant modules |
michael@0 | 1537 | * will return CKR_NOT_INITIALIZED */ |
michael@0 | 1538 | (void) PK11_GETTAB(mod)->C_Finalize(NULL); |
michael@0 | 1539 | /* now initialize the module, this function reinitializes |
michael@0 | 1540 | * a module in place, preserving existing slots (even if they |
michael@0 | 1541 | * no longer exist) */ |
michael@0 | 1542 | rv = secmod_ModuleInit(mod, NULL, &alreadyLoaded); |
michael@0 | 1543 | if (rv != SECSuccess) { |
michael@0 | 1544 | /* save the last error code */ |
michael@0 | 1545 | lastError = PORT_GetError(); |
michael@0 | 1546 | rrv = rv; |
michael@0 | 1547 | /* couldn't reinit the module, disable all its slots */ |
michael@0 | 1548 | for (i=0; i < mod->slotCount; i++) { |
michael@0 | 1549 | mod->slots[i]->disabled = PR_TRUE; |
michael@0 | 1550 | mod->slots[i]->reason = PK11_DIS_COULD_NOT_INIT_TOKEN; |
michael@0 | 1551 | } |
michael@0 | 1552 | continue; |
michael@0 | 1553 | } |
michael@0 | 1554 | for (i=0; i < mod->slotCount; i++) { |
michael@0 | 1555 | /* get new token sessions, bump the series up so that |
michael@0 | 1556 | * we refresh other old sessions. This will tell much of |
michael@0 | 1557 | * NSS to flush cached handles it may hold as well */ |
michael@0 | 1558 | rv = PK11_InitToken(mod->slots[i],PR_TRUE); |
michael@0 | 1559 | /* PK11_InitToken could fail if the slot isn't present. |
michael@0 | 1560 | * If it is present, though, something is wrong and we should |
michael@0 | 1561 | * disable the slot and let the caller know. */ |
michael@0 | 1562 | if (rv != SECSuccess && PK11_IsPresent(mod->slots[i])) { |
michael@0 | 1563 | /* save the last error code */ |
michael@0 | 1564 | lastError = PORT_GetError(); |
michael@0 | 1565 | rrv = rv; |
michael@0 | 1566 | /* disable the token */ |
michael@0 | 1567 | mod->slots[i]->disabled = PR_TRUE; |
michael@0 | 1568 | mod->slots[i]->reason = PK11_DIS_COULD_NOT_INIT_TOKEN; |
michael@0 | 1569 | } |
michael@0 | 1570 | } |
michael@0 | 1571 | } |
michael@0 | 1572 | } |
michael@0 | 1573 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 1574 | |
michael@0 | 1575 | /* |
michael@0 | 1576 | * on multiple failures, we are only returning the lastError. The caller |
michael@0 | 1577 | * can determine which slots are bad by calling PK11_IsDisabled(). |
michael@0 | 1578 | */ |
michael@0 | 1579 | if (rrv != SECSuccess) { |
michael@0 | 1580 | /* restore the last error code */ |
michael@0 | 1581 | PORT_SetError(lastError); |
michael@0 | 1582 | } |
michael@0 | 1583 | |
michael@0 | 1584 | return rrv; |
michael@0 | 1585 | } |