tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pk11load.c (24175B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 /*
      5 * The following handles the loading, unloading and management of
      6 * various PCKS #11 modules
      7 */
      8 #define FORCE_PR_LOG 1
      9 #include "base.h"
     10 #include "seccomon.h"
     11 #include "pkcs11.h"
     12 #include "secmod.h"
     13 #include "prlink.h"
     14 #include "pk11func.h"
     15 #include "secmodi.h"
     16 #include "secmodti.h"
     17 #include "nssilock.h"
     18 #include "secerr.h"
     19 #include "prenv.h"
     20 #include "utilpars.h"
     21 #include "prio.h"
     22 #include "prprf.h"
     23 #include <stdio.h>
     24 #include "prsystem.h"
     25 
     26 #define DEBUG_MODULE 1
     27 
     28 #ifdef DEBUG_MODULE
     29 static char *modToDBG = NULL;
     30 
     31 #include "debug_module.c"
     32 #endif
     33 
     34 /* build the PKCS #11 2.01 lock files */
     35 CK_RV PR_CALLBACK
     36 secmodCreateMutext(CK_VOID_PTR_PTR pmutex)
     37 {
     38    *pmutex = (CK_VOID_PTR)PZ_NewLock(nssILockOther);
     39    if (*pmutex)
     40        return CKR_OK;
     41    return CKR_HOST_MEMORY;
     42 }
     43 
     44 CK_RV PR_CALLBACK
     45 secmodDestroyMutext(CK_VOID_PTR mutext)
     46 {
     47    PZ_DestroyLock((PZLock *)mutext);
     48    return CKR_OK;
     49 }
     50 
     51 CK_RV PR_CALLBACK
     52 secmodLockMutext(CK_VOID_PTR mutext)
     53 {
     54    PZ_Lock((PZLock *)mutext);
     55    return CKR_OK;
     56 }
     57 
     58 CK_RV PR_CALLBACK
     59 secmodUnlockMutext(CK_VOID_PTR mutext)
     60 {
     61    PZ_Unlock((PZLock *)mutext);
     62    return CKR_OK;
     63 }
     64 
     65 static SECMODModuleID nextModuleID = 1;
     66 static const CK_C_INITIALIZE_ARGS secmodLockFunctions = {
     67    secmodCreateMutext, secmodDestroyMutext, secmodLockMutext,
     68    secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS | CKF_OS_LOCKING_OK,
     69    NULL
     70 };
     71 static const CK_C_INITIALIZE_ARGS secmodNoLockArgs = {
     72    NULL, NULL, NULL, NULL,
     73    CKF_LIBRARY_CANT_CREATE_OS_THREADS, NULL
     74 };
     75 
     76 static PRBool loadSingleThreadedModules = PR_TRUE;
     77 static PRBool enforceAlreadyInitializedError = PR_TRUE;
     78 static PRBool finalizeModules = PR_TRUE;
     79 
     80 /* set global options for NSS PKCS#11 module loader */
     81 SECStatus
     82 pk11_setGlobalOptions(PRBool noSingleThreadedModules,
     83                      PRBool allowAlreadyInitializedModules,
     84                      PRBool dontFinalizeModules)
     85 {
     86    if (noSingleThreadedModules) {
     87        loadSingleThreadedModules = PR_FALSE;
     88    } else {
     89        loadSingleThreadedModules = PR_TRUE;
     90    }
     91    if (allowAlreadyInitializedModules) {
     92        enforceAlreadyInitializedError = PR_FALSE;
     93    } else {
     94        enforceAlreadyInitializedError = PR_TRUE;
     95    }
     96    if (dontFinalizeModules) {
     97        finalizeModules = PR_FALSE;
     98    } else {
     99        finalizeModules = PR_TRUE;
    100    }
    101    return SECSuccess;
    102 }
    103 
    104 PRBool
    105 pk11_getFinalizeModulesOption(void)
    106 {
    107    return finalizeModules;
    108 }
    109 
    110 /*
    111 * Allow specification loading the same module more than once at init time.
    112 * This enables 2 things.
    113 *
    114 *    1) we can load additional databases by manipulating secmod.db/pkcs11.txt.
    115 *    2) we can handle the case where some library has already initialized NSS
    116 *    before the main application.
    117 *
    118 * oldModule is the module we have already initialized.
    119 * char *modulespec is the full module spec for the library we want to
    120 * initialize.
    121 */
    122 static SECStatus
    123 secmod_handleReload(SECMODModule *oldModule, SECMODModule *newModule)
    124 {
    125    PK11SlotInfo *slot;
    126    char *modulespec;
    127    char *newModuleSpec;
    128    char **children;
    129    CK_SLOT_ID *ids;
    130    SECMODConfigList *conflist = NULL;
    131    SECStatus rv = SECFailure;
    132    int count = 0;
    133 
    134    /* first look for tokens= key words from the module spec */
    135    modulespec = newModule->libraryParams;
    136    newModuleSpec = secmod_ParseModuleSpecForTokens(PR_TRUE,
    137                                                    newModule->isFIPS, modulespec, &children, &ids);
    138    if (!newModuleSpec) {
    139        return SECFailure;
    140    }
    141 
    142    /*
    143     * We are now trying to open a new slot on an already loaded module.
    144     * If that slot represents a cert/key database, we don't want to open
    145     * multiple copies of that same database. Unfortunately we understand
    146     * the softoken flags well enough to be able to do this, so we can only get
    147     * the list of already loaded databases if we are trying to open another
    148     * internal module.
    149     */
    150    if (oldModule->internal) {
    151        conflist = secmod_GetConfigList(oldModule->isFIPS,
    152                                        oldModule->libraryParams, &count);
    153    }
    154 
    155    /* don't open multiple of the same db */
    156    if (conflist && secmod_MatchConfigList(newModuleSpec, conflist, count)) {
    157        rv = SECSuccess;
    158        goto loser;
    159    }
    160    slot = SECMOD_OpenNewSlot(oldModule, newModuleSpec);
    161    if (slot) {
    162        int newID;
    163        char **thisChild;
    164        CK_SLOT_ID *thisID;
    165        char *oldModuleSpec;
    166 
    167        if (secmod_IsInternalKeySlot(newModule)) {
    168            pk11_SetInternalKeySlotIfFirst(slot);
    169        }
    170        newID = slot->slotID;
    171        PK11_FreeSlot(slot);
    172        for (thisChild = children, thisID = ids; thisChild && *thisChild;
    173             thisChild++, thisID++) {
    174            if (conflist &&
    175                secmod_MatchConfigList(*thisChild, conflist, count)) {
    176                *thisID = (CK_SLOT_ID)-1;
    177                continue;
    178            }
    179            slot = SECMOD_OpenNewSlot(oldModule, *thisChild);
    180            if (slot) {
    181                *thisID = slot->slotID;
    182                PK11_FreeSlot(slot);
    183            } else {
    184                *thisID = (CK_SLOT_ID)-1;
    185            }
    186        }
    187 
    188        /* update the old module initialization string in case we need to
    189         * shutdown and reinit the whole mess (this is rare, but can happen
    190         * when trying to stop smart card insertion/removal threads)... */
    191        oldModuleSpec = secmod_MkAppendTokensList(oldModule->arena,
    192                                                  oldModule->libraryParams, newModuleSpec, newID,
    193                                                  children, ids);
    194        if (oldModuleSpec) {
    195            oldModule->libraryParams = oldModuleSpec;
    196        }
    197 
    198        rv = SECSuccess;
    199    }
    200 
    201 loser:
    202    secmod_FreeChildren(children, ids);
    203    PORT_Free(newModuleSpec);
    204    if (conflist) {
    205        secmod_FreeConfigList(conflist, count);
    206    }
    207    return rv;
    208 }
    209 
    210 /*
    211 * collect the steps we need to initialize a module in a single function
    212 */
    213 SECStatus
    214 secmod_ModuleInit(SECMODModule *mod, SECMODModule **reload,
    215                  PRBool *alreadyLoaded)
    216 {
    217    CK_C_INITIALIZE_ARGS moduleArgs;
    218    CK_VOID_PTR pInitArgs;
    219    CK_RV crv;
    220 
    221    if (reload) {
    222        *reload = NULL;
    223    }
    224 
    225    if (!mod || !alreadyLoaded) {
    226        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    227        return SECFailure;
    228    }
    229 
    230    if (mod->libraryParams == NULL) {
    231        if (mod->isThreadSafe) {
    232            pInitArgs = (void *)&secmodLockFunctions;
    233        } else {
    234            pInitArgs = NULL;
    235        }
    236    } else {
    237        if (mod->isThreadSafe) {
    238            moduleArgs = secmodLockFunctions;
    239        } else {
    240            moduleArgs = secmodNoLockArgs;
    241        }
    242        moduleArgs.LibraryParameters = (void *)mod->libraryParams;
    243        pInitArgs = &moduleArgs;
    244    }
    245    crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs);
    246    if (CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) {
    247        SECMODModule *oldModule = NULL;
    248 
    249        /* Library has already been loaded once, if caller expects it, and it
    250         * has additional configuration, try reloading it as well. */
    251        if (reload != NULL && mod->libraryParams) {
    252            oldModule = secmod_FindModuleByFuncPtr(mod->functionList);
    253        }
    254        /* Library has been loaded by NSS. It means it may be capable of
    255         * reloading */
    256        if (oldModule) {
    257            SECStatus rv;
    258            rv = secmod_handleReload(oldModule, mod);
    259            if (rv == SECSuccess) {
    260                /* This module should go away soon, since we've
    261                 * simply expanded the slots on the old module.
    262                 * When it goes away, it should not Finalize since
    263                 * that will close our old module as well. Setting
    264                 * the function list to NULL will prevent that close */
    265                mod->functionList = NULL;
    266                *reload = oldModule;
    267                return SECSuccess;
    268            }
    269            SECMOD_DestroyModule(oldModule);
    270        }
    271        /* reload not possible, fall back to old semantics */
    272        if (!enforceAlreadyInitializedError) {
    273            *alreadyLoaded = PR_TRUE;
    274            return SECSuccess;
    275        }
    276    }
    277    if (crv != CKR_OK) {
    278        if (!mod->isThreadSafe ||
    279            crv == CKR_NSS_CERTDB_FAILED ||
    280            crv == CKR_NSS_KEYDB_FAILED) {
    281            PORT_SetError(PK11_MapError(crv));
    282            return SECFailure;
    283        }
    284        /* If we had attempted to init a single threaded module "with"
    285         * parameters and it failed, should we retry "without" parameters?
    286         * (currently we don't retry in this scenario) */
    287 
    288        if (!loadSingleThreadedModules) {
    289            PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
    290            return SECFailure;
    291        }
    292        /* If we arrive here, the module failed a ThreadSafe init. */
    293        mod->isThreadSafe = PR_FALSE;
    294        if (!mod->libraryParams) {
    295            pInitArgs = NULL;
    296        } else {
    297            moduleArgs = secmodNoLockArgs;
    298            moduleArgs.LibraryParameters = (void *)mod->libraryParams;
    299            pInitArgs = &moduleArgs;
    300        }
    301        crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs);
    302        if ((CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) &&
    303            (!enforceAlreadyInitializedError)) {
    304            *alreadyLoaded = PR_TRUE;
    305            return SECSuccess;
    306        }
    307        if (crv != CKR_OK) {
    308            PORT_SetError(PK11_MapError(crv));
    309            return SECFailure;
    310        }
    311    }
    312    return SECSuccess;
    313 }
    314 
    315 /*
    316 * set the hasRootCerts flags in the module so it can be stored back
    317 * into the database.
    318 */
    319 void
    320 SECMOD_SetRootCerts(PK11SlotInfo *slot, SECMODModule *mod)
    321 {
    322    PK11PreSlotInfo *psi = NULL;
    323    int i;
    324 
    325    if (slot->hasRootCerts) {
    326        for (i = 0; i < mod->slotInfoCount; i++) {
    327            if (slot->slotID == mod->slotInfo[i].slotID) {
    328                psi = &mod->slotInfo[i];
    329                break;
    330            }
    331        }
    332        if (psi == NULL) {
    333            /* allocate more slots */
    334            PK11PreSlotInfo *psi_list = (PK11PreSlotInfo *)
    335                PORT_ArenaAlloc(mod->arena,
    336                                (mod->slotInfoCount + 1) * sizeof(PK11PreSlotInfo));
    337            /* copy the old ones */
    338            if (mod->slotInfoCount > 0) {
    339                PORT_Memcpy(psi_list, mod->slotInfo,
    340                            (mod->slotInfoCount) * sizeof(PK11PreSlotInfo));
    341            }
    342            /* assign psi to the last new slot */
    343            psi = &psi_list[mod->slotInfoCount];
    344            psi->slotID = slot->slotID;
    345            psi->askpw = 0;
    346            psi->timeout = 0;
    347            psi->defaultFlags = 0;
    348 
    349            /* increment module count & store new list */
    350            mod->slotInfo = psi_list;
    351            mod->slotInfoCount++;
    352        }
    353        psi->hasRootCerts = 1;
    354    }
    355 }
    356 
    357 #ifndef NSS_STATIC_SOFTOKEN
    358 static const char *my_shlib_name =
    359    SHLIB_PREFIX "nss" NSS_SHLIB_VERSION "." SHLIB_SUFFIX;
    360 static const char *softoken_shlib_name =
    361    SHLIB_PREFIX "softokn" SOFTOKEN_SHLIB_VERSION "." SHLIB_SUFFIX;
    362 static const PRCallOnceType pristineCallOnce;
    363 static PRCallOnceType loadSoftokenOnce;
    364 static PRLibrary *softokenLib;
    365 static PRInt32 softokenLoadCount;
    366 
    367 /* This function must be run only once. */
    368 /*  determine if hybrid platform, then actually load the DSO. */
    369 static PRStatus
    370 softoken_LoadDSO(void)
    371 {
    372    PRLibrary *handle;
    373 
    374    handle = PORT_LoadLibraryFromOrigin(my_shlib_name,
    375                                        (PRFuncPtr)&softoken_LoadDSO,
    376                                        softoken_shlib_name);
    377    if (handle) {
    378        softokenLib = handle;
    379        return PR_SUCCESS;
    380    }
    381    return PR_FAILURE;
    382 }
    383 #else
    384 CK_RV NSC_GetInterface(CK_UTF8CHAR_PTR pInterfaceName,
    385                       CK_VERSION_PTR pVersion,
    386                       CK_INTERFACE_PTR_PTR *ppInterface, CK_FLAGS flags);
    387 char **NSC_ModuleDBFunc(unsigned long function, char *parameters, void *args);
    388 #endif
    389 
    390 SECStatus
    391 secmod_DetermineModuleFunctionList(SECMODModule *mod)
    392 {
    393    PRLibrary *library = NULL;
    394    CK_C_GetInterface ientry = NULL;
    395    CK_C_GetFunctionList fentry = NULL;
    396    char *disableUnload = NULL;
    397 #ifndef NSS_STATIC_SOFTOKEN
    398    const char *nss_interface;
    399    const char *nss_function;
    400 #endif
    401    CK_INTERFACE_PTR interface;
    402 
    403    /* internal modules get loaded from their internal list */
    404    if (mod->internal && (mod->dllName == NULL)) {
    405 #ifdef NSS_STATIC_SOFTOKEN
    406        ientry = (CK_C_GetInterface)NSC_GetInterface;
    407 #else
    408        /*
    409         * Loads softoken as a dynamic library,
    410         * even though the rest of NSS assumes this as the "internal" module.
    411         */
    412        if (!softokenLib &&
    413            PR_SUCCESS != PR_CallOnce(&loadSoftokenOnce, &softoken_LoadDSO))
    414            return SECFailure;
    415 
    416        PR_ATOMIC_INCREMENT(&softokenLoadCount);
    417 
    418        if (mod->isFIPS) {
    419            nss_interface = "FC_GetInterface";
    420            nss_function = "FC_GetFunctionList";
    421        } else {
    422            nss_interface = "NSC_GetInterface";
    423            nss_function = "NSC_GetFunctionList";
    424        }
    425 
    426        ientry = (CK_C_GetInterface)
    427            PR_FindSymbol(softokenLib, nss_interface);
    428        if (!ientry) {
    429            fentry = (CK_C_GetFunctionList)
    430                PR_FindSymbol(softokenLib, nss_function);
    431            if (!fentry) {
    432                return SECFailure;
    433            }
    434        }
    435 #endif
    436 
    437        if (mod->isModuleDB) {
    438            mod->moduleDBFunc = (CK_C_GetFunctionList)
    439 #ifdef NSS_STATIC_SOFTOKEN
    440                NSC_ModuleDBFunc;
    441 #else
    442                PR_FindSymbol(softokenLib, "NSC_ModuleDBFunc");
    443 #endif
    444        }
    445 
    446        if (mod->moduleDBOnly) {
    447            mod->loaded = PR_TRUE;
    448            return SECSuccess;
    449        }
    450    } else {
    451        /* Not internal, load the DLL and look up C_GetFunctionList */
    452        if (mod->dllName == NULL) {
    453            return SECFailure;
    454        }
    455 
    456 /* load the library. If this succeeds, then we have to remember to
    457 * unload the library if anything goes wrong from here on out...
    458 */
    459 #if defined(_WIN32)
    460        if (nssUTF8_Length(mod->dllName, NULL)) {
    461            wchar_t *dllNameWide = _NSSUTIL_UTF8ToWide(mod->dllName);
    462            if (dllNameWide) {
    463                PRLibSpec libSpec;
    464                libSpec.type = PR_LibSpec_PathnameU;
    465                libSpec.value.pathname_u = dllNameWide;
    466                library = PR_LoadLibraryWithFlags(libSpec, 0);
    467                PORT_Free(dllNameWide);
    468            }
    469        }
    470        if (library == NULL) {
    471            // fallback to system code page
    472            library = PR_LoadLibrary(mod->dllName);
    473        }
    474 #else
    475        library = PR_LoadLibrary(mod->dllName);
    476 #endif // defined(_WIN32)
    477        mod->library = (void *)library;
    478 
    479        if (library == NULL) {
    480            return SECFailure;
    481        }
    482 
    483        /*
    484         * now we need to get the entry point to find the function pointers
    485         */
    486        if (!mod->moduleDBOnly) {
    487            ientry = (CK_C_GetInterface)
    488                PR_FindSymbol(library, "C_GetInterface");
    489            if (!ientry) {
    490                fentry = (CK_C_GetFunctionList)
    491                    PR_FindSymbol(library, "C_GetFunctionList");
    492            }
    493        }
    494        if (mod->isModuleDB) {
    495            mod->moduleDBFunc = (void *)
    496                PR_FindSymbol(library, "NSS_ReturnModuleSpecData");
    497        }
    498        if (mod->moduleDBFunc == NULL)
    499            mod->isModuleDB = PR_FALSE;
    500        if ((ientry == NULL) && (fentry == NULL)) {
    501            if (mod->isModuleDB) {
    502                mod->loaded = PR_TRUE;
    503                mod->moduleDBOnly = PR_TRUE;
    504                return SECSuccess;
    505            }
    506            PR_UnloadLibrary(library);
    507            return SECFailure;
    508        }
    509    }
    510 
    511    /*
    512     * We need to get the function list
    513     */
    514    if (ientry) {
    515        /* we first try to get a FORK_SAFE interface */
    516        if ((*ientry)((CK_UTF8CHAR_PTR) "PKCS 11", NULL, &interface,
    517                      CKF_INTERFACE_FORK_SAFE) != CKR_OK) {
    518            /* one is not appearantly available, get a non-fork safe version */
    519            if ((*ientry)((CK_UTF8CHAR_PTR) "PKCS 11", NULL, &interface, 0) != CKR_OK) {
    520                goto fail;
    521            }
    522        }
    523        mod->functionList = interface->pFunctionList;
    524        mod->flags = interface->flags;
    525        /* if we have a fips indicator, grab it */
    526        if ((*ientry)((CK_UTF8CHAR_PTR) "Vendor NSS FIPS Interface", NULL,
    527                      &interface, 0) == CKR_OK) {
    528            mod->fipsIndicator = ((CK_NSS_FIPS_FUNCTIONS *)(interface->pFunctionList))->NSC_NSSGetFIPSStatus;
    529        }
    530    } else {
    531        if ((*fentry)((CK_FUNCTION_LIST_PTR *)&mod->functionList) != CKR_OK)
    532            goto fail;
    533        mod->flags = 0;
    534    }
    535 
    536 #ifdef DEBUG_MODULE
    537    modToDBG = PR_GetEnvSecure("NSS_DEBUG_PKCS11_MODULE");
    538    if (modToDBG && strcmp(mod->commonName, modToDBG) == 0) {
    539        mod->functionList = (void *)nss_InsertDeviceLog(
    540            (CK_FUNCTION_LIST_3_0_PTR)mod->functionList);
    541    }
    542 #endif
    543 
    544    return SECSuccess;
    545 
    546 fail:
    547    mod->functionList = NULL;
    548    disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
    549    if (library && !disableUnload) {
    550        PR_UnloadLibrary(library);
    551    }
    552    return SECFailure;
    553 }
    554 
    555 SECStatus
    556 secmod_InitializeModuleAndGetSlotInfo(SECMODModule *mod, SECMODModule **oldModule)
    557 {
    558    CK_INFO info;
    559    CK_ULONG slotCount = 0;
    560    SECStatus rv;
    561    PRBool alreadyLoaded = PR_FALSE;
    562 
    563    /* This test operation makes sure our locking system is
    564     * consistent even if we are using non-thread safe tokens by
    565     * simulating unsafe tokens with safe ones. */
    566    mod->isThreadSafe = !PR_GetEnvSecure("NSS_FORCE_TOKEN_LOCK");
    567 
    568    /* Now we initialize the module */
    569    rv = secmod_ModuleInit(mod, oldModule, &alreadyLoaded);
    570    if (rv != SECSuccess) {
    571        goto fail;
    572    }
    573 
    574    /* module has been reloaded, this module itself is done,
    575     * return to the caller */
    576    if (mod->functionList == NULL) {
    577        mod->loaded = PR_TRUE; /* technically the module is loaded.. */
    578        return SECSuccess;
    579    }
    580 
    581    /* check the version number */
    582    if (PK11_GETTAB(mod)->C_GetInfo(&info) != CKR_OK)
    583        goto fail2;
    584    if (info.cryptokiVersion.major < 2)
    585        goto fail2;
    586    /* all 2.0 are a priori *not* thread safe */
    587    if ((info.cryptokiVersion.major == 2) && (info.cryptokiVersion.minor < 1)) {
    588        if (!loadSingleThreadedModules) {
    589            PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
    590            goto fail2;
    591        } else {
    592            mod->isThreadSafe = PR_FALSE;
    593        }
    594    }
    595    mod->cryptokiVersion = info.cryptokiVersion;
    596 
    597    /* If we don't have a common name, get it from the PKCS 11 module */
    598    if ((mod->commonName == NULL) || (mod->commonName[0] == 0)) {
    599        mod->commonName = PK11_MakeString(mod->arena, NULL,
    600                                          (char *)info.libraryDescription, sizeof(info.libraryDescription));
    601        if (mod->commonName == NULL)
    602            goto fail2;
    603    }
    604 
    605    /* initialize the Slots */
    606    if (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &slotCount) == CKR_OK) {
    607        CK_SLOT_ID *slotIDs;
    608        int i;
    609        CK_RV crv;
    610 
    611        mod->slots = (PK11SlotInfo **)PORT_ArenaAlloc(mod->arena,
    612                                                      sizeof(PK11SlotInfo *) * slotCount);
    613        if (mod->slots == NULL)
    614            goto fail2;
    615 
    616        slotIDs = (CK_SLOT_ID *)PORT_Alloc(sizeof(CK_SLOT_ID) * slotCount);
    617        if (slotIDs == NULL) {
    618            goto fail2;
    619        }
    620        crv = PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, slotIDs, &slotCount);
    621        if (crv != CKR_OK) {
    622            PORT_Free(slotIDs);
    623            goto fail2;
    624        }
    625 
    626        /* Initialize each slot */
    627        for (i = 0; i < (int)slotCount; i++) {
    628            mod->slots[i] = PK11_NewSlotInfo(mod);
    629            PK11_InitSlot(mod, slotIDs[i], mod->slots[i]);
    630            /* look down the slot info table */
    631            PK11_LoadSlotList(mod->slots[i], mod->slotInfo, mod->slotInfoCount);
    632            SECMOD_SetRootCerts(mod->slots[i], mod);
    633            /* explicitly mark the internal slot as such if IsInternalKeySlot()
    634             * is set */
    635            if (secmod_IsInternalKeySlot(mod) && (i == (mod->isFIPS ? 0 : 1))) {
    636                pk11_SetInternalKeySlotIfFirst(mod->slots[i]);
    637            }
    638        }
    639        mod->slotCount = slotCount;
    640        mod->slotInfoCount = 0;
    641        PORT_Free(slotIDs);
    642    }
    643 
    644    mod->loaded = PR_TRUE;
    645    mod->moduleID = nextModuleID++;
    646    return SECSuccess;
    647 fail2:
    648    if (enforceAlreadyInitializedError || (!alreadyLoaded)) {
    649        PK11_GETTAB(mod)->C_Finalize(NULL);
    650    }
    651 fail:
    652    mod->functionList = NULL;
    653    return SECFailure;
    654 }
    655 
    656 /*
    657 * load a new module into our address space and initialize it.
    658 */
    659 SECStatus
    660 secmod_LoadPKCS11Module(SECMODModule *mod, SECMODModule **oldModule)
    661 {
    662    SECStatus rv = SECFailure;
    663    if (mod->loaded) {
    664        return SECSuccess;
    665    }
    666 
    667    mod->fipsIndicator = NULL;
    668 
    669    rv = secmod_DetermineModuleFunctionList(mod);
    670    if (rv != SECSuccess) { // The error code is set up by secmod_DetermineModuleFunctionList.
    671        return rv;
    672    }
    673 
    674    if (mod->loaded == PR_TRUE) {
    675        return SECSuccess;
    676    }
    677 
    678    rv = secmod_InitializeModuleAndGetSlotInfo(mod, oldModule);
    679    if (rv != SECSuccess) { // The error code is set up by secmod_InitializeModuleAndGetSlotInfo
    680        return rv;
    681    }
    682 
    683    return SECSuccess;
    684 }
    685 
    686 /*
    687 * load a new module using provided fentry function
    688 */
    689 SECStatus
    690 secmod_LoadPKCS11ModuleFromFunction(SECMODModule *mod, SECMODModule **oldModule,
    691                                    CK_C_GetFunctionList fentry)
    692 {
    693    SECStatus rv = SECFailure;
    694    CK_RV crv;
    695    if (mod->loaded) {
    696        return SECSuccess;
    697    }
    698 
    699    mod->fipsIndicator = NULL;
    700 
    701    if (!fentry) {
    702        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    703        return SECFailure;
    704    }
    705 
    706    crv = fentry((CK_FUNCTION_LIST_PTR *)&mod->functionList);
    707    if (crv != CKR_OK) {
    708        mod->functionList = NULL;
    709        PORT_SetError(PK11_MapError(crv));
    710        return SECFailure;
    711    }
    712 
    713    if (mod->functionList == NULL) {
    714        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
    715        return SECFailure;
    716    }
    717 
    718    mod->flags = 0;
    719    rv = secmod_InitializeModuleAndGetSlotInfo(mod, oldModule);
    720    if (rv != SECSuccess) {
    721        return rv;
    722    }
    723 
    724    return SECSuccess;
    725 }
    726 
    727 SECStatus
    728 SECMOD_UnloadModule(SECMODModule *mod)
    729 {
    730    PRLibrary *library;
    731    char *disableUnload = NULL;
    732 
    733    if (!mod->loaded) {
    734        return SECFailure;
    735    }
    736    if (finalizeModules) {
    737        if (mod->functionList && !mod->moduleDBOnly) {
    738            PK11_GETTAB(mod)->C_Finalize(NULL);
    739        }
    740    }
    741    mod->moduleID = 0;
    742    mod->loaded = PR_FALSE;
    743 
    744    /* do we want the semantics to allow unloading the internal library?
    745     * if not, we should change this to SECFailure and move it above the
    746     * mod->loaded = PR_FALSE; */
    747    if (mod->internal && (mod->dllName == NULL)) {
    748 #ifndef NSS_STATIC_SOFTOKEN
    749        if (0 == PR_ATOMIC_DECREMENT(&softokenLoadCount)) {
    750            if (softokenLib) {
    751                disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
    752                if (!disableUnload) {
    753 #ifdef DEBUG
    754                    PRStatus status = PR_UnloadLibrary(softokenLib);
    755                    PORT_Assert(PR_SUCCESS == status);
    756 #else
    757                    PR_UnloadLibrary(softokenLib);
    758 #endif
    759                }
    760                softokenLib = NULL;
    761            }
    762            loadSoftokenOnce = pristineCallOnce;
    763        }
    764 #endif
    765        return SECSuccess;
    766    }
    767 
    768    library = (PRLibrary *)mod->library;
    769    /* if no library, then we should not unload it */
    770    if (library == NULL) {
    771        return SECSuccess;
    772    }
    773 
    774    disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
    775    if (!disableUnload) {
    776        PR_UnloadLibrary(library);
    777    }
    778    return SECSuccess;
    779 }
    780 
    781 void
    782 nss_DumpModuleLog(void)
    783 {
    784 #ifdef DEBUG_MODULE
    785    if (modToDBG) {
    786        print_final_statistics();
    787    }
    788 #endif
    789 }