Back to index

lightning-sunbird  0.9+nobinonly
pk11load.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 /*
00037  * The following handles the loading, unloading and management of
00038  * various PCKS #11 modules
00039  */
00040 #include "seccomon.h"
00041 #include "pkcs11.h"
00042 #include "secmod.h"
00043 #include "prlink.h"
00044 #include "pk11func.h"
00045 #include "secmodi.h"
00046 #include "secmodti.h"
00047 #include "nssilock.h"
00048 #include "secerr.h"
00049 #include "prenv.h"
00050 
00051 extern void FC_GetFunctionList(void);
00052 extern void NSC_GetFunctionList(void);
00053 extern void NSC_ModuleDBFunc(void);
00054 
00055 #ifdef DEBUG
00056 #define DEBUG_MODULE 1
00057 #endif
00058 
00059 #ifdef DEBUG_MODULE
00060 static char *modToDBG = NULL;
00061 
00062 #include "debug_module.c"
00063 #endif
00064 
00065 /* build the PKCS #11 2.01 lock files */
00066 CK_RV PR_CALLBACK secmodCreateMutext(CK_VOID_PTR_PTR pmutex) {
00067     *pmutex = (CK_VOID_PTR) PZ_NewLock(nssILockOther);
00068     if ( *pmutex ) return CKR_OK;
00069     return CKR_HOST_MEMORY;
00070 }
00071 
00072 CK_RV PR_CALLBACK secmodDestroyMutext(CK_VOID_PTR mutext) {
00073     PZ_DestroyLock((PZLock *)mutext);
00074     return CKR_OK;
00075 }
00076 
00077 CK_RV PR_CALLBACK secmodLockMutext(CK_VOID_PTR mutext) {
00078     PZ_Lock((PZLock *)mutext);
00079     return CKR_OK;
00080 }
00081 
00082 CK_RV PR_CALLBACK secmodUnlockMutext(CK_VOID_PTR mutext) {
00083     PZ_Unlock((PZLock *)mutext);
00084     return CKR_OK;
00085 }
00086 
00087 static SECMODModuleID  nextModuleID = 1;
00088 static const CK_C_INITIALIZE_ARGS secmodLockFunctions = {
00089     secmodCreateMutext, secmodDestroyMutext, secmodLockMutext, 
00090     secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS|
00091        CKF_OS_LOCKING_OK
00092     ,NULL
00093 };
00094 
00095 static PRBool loadSingleThreadedModules = PR_TRUE;
00096 static PRBool enforceAlreadyInitializedError = PR_TRUE;
00097 static PRBool finalizeModules = PR_TRUE;
00098 
00099 /* set global options for NSS PKCS#11 module loader */
00100 SECStatus pk11_setGlobalOptions(PRBool noSingleThreadedModules,
00101                                 PRBool allowAlreadyInitializedModules,
00102                                 PRBool dontFinalizeModules)
00103 {
00104     if (noSingleThreadedModules) {
00105         loadSingleThreadedModules = PR_FALSE;
00106     } else {
00107         loadSingleThreadedModules = PR_TRUE;
00108     }
00109     if (allowAlreadyInitializedModules) {
00110         enforceAlreadyInitializedError = PR_FALSE;
00111     } else {
00112         enforceAlreadyInitializedError = PR_TRUE;
00113     }
00114     if (dontFinalizeModules) {
00115         finalizeModules = PR_FALSE;
00116     } else {
00117         finalizeModules = PR_TRUE;
00118     }
00119     return SECSuccess;
00120 }
00121 
00122 PRBool pk11_getFinalizeModulesOption(void)
00123 {
00124     return finalizeModules;
00125 }
00126 
00127 /*
00128  * collect the steps we need to initialize a module in a single function
00129  */
00130 SECStatus
00131 secmod_ModuleInit(SECMODModule *mod, PRBool* alreadyLoaded)
00132 {
00133     CK_C_INITIALIZE_ARGS moduleArgs;
00134     CK_VOID_PTR pInitArgs;
00135     CK_RV crv;
00136 
00137     if (!mod || !alreadyLoaded) {
00138         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00139         return SECFailure;
00140     }
00141 
00142     if (mod->isThreadSafe == PR_FALSE) {
00143        pInitArgs = NULL;
00144     } else if (mod->libraryParams == NULL) {
00145        pInitArgs = (void *) &secmodLockFunctions;
00146     } else {
00147        moduleArgs = secmodLockFunctions;
00148        moduleArgs.LibraryParameters = (void *) mod->libraryParams;
00149        pInitArgs = &moduleArgs;
00150     }
00151     crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs);
00152     if ((CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) &&
00153         (!enforceAlreadyInitializedError)) {
00154         *alreadyLoaded = PR_TRUE;
00155         return SECSuccess;
00156     }
00157     if (crv != CKR_OK) {
00158        if (pInitArgs == NULL ||
00159               crv == CKR_NETSCAPE_CERTDB_FAILED ||
00160               crv == CKR_NETSCAPE_KEYDB_FAILED) {
00161            PORT_SetError(PK11_MapError(crv));
00162            return SECFailure;
00163        }
00164        if (!loadSingleThreadedModules) {
00165            PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
00166            return SECFailure;
00167        }
00168        mod->isThreadSafe = PR_FALSE;
00169        crv = PK11_GETTAB(mod)->C_Initialize(NULL);
00170        if ((CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) &&
00171            (!enforceAlreadyInitializedError)) {
00172            *alreadyLoaded = PR_TRUE;
00173            return SECSuccess;
00174        }
00175        if (crv != CKR_OK)  {
00176            PORT_SetError(PK11_MapError(crv));
00177            return SECFailure;
00178        }
00179     }
00180     return SECSuccess;
00181 }
00182 
00183 /*
00184  * set the hasRootCerts flags in the module so it can be stored back
00185  * into the database.
00186  */
00187 void
00188 SECMOD_SetRootCerts(PK11SlotInfo *slot, SECMODModule *mod) {
00189     PK11PreSlotInfo *psi = NULL;
00190     int i;
00191 
00192     if (slot->hasRootCerts) {
00193        for (i=0; i < mod->slotInfoCount; i++) {
00194            if (slot->slotID == mod->slotInfo[i].slotID) {
00195               psi = &mod->slotInfo[i];
00196               break;
00197            }
00198        }
00199        if (psi == NULL) {
00200           /* allocate more slots */
00201           PK11PreSlotInfo *psi_list = (PK11PreSlotInfo *)
00202               PORT_ArenaAlloc(mod->arena,
00203                      (mod->slotInfoCount+1)* sizeof(PK11PreSlotInfo));
00204           /* copy the old ones */
00205           if (mod->slotInfoCount > 0) {
00206               PORT_Memcpy(psi_list,mod->slotInfo,
00207                             (mod->slotInfoCount)*sizeof(PK11PreSlotInfo));
00208           }
00209           /* assign psi to the last new slot */
00210           psi = &psi_list[mod->slotInfoCount];
00211           psi->slotID = slot->slotID;
00212           psi->askpw = 0;
00213           psi->timeout = 0;
00214           psi ->defaultFlags = 0;
00215 
00216           /* increment module count & store new list */
00217           mod->slotInfo = psi_list;
00218           mod->slotInfoCount++;
00219           
00220        }
00221        psi->hasRootCerts = 1;
00222     }
00223 }
00224 
00225 /*
00226  * load a new module into our address space and initialize it.
00227  */
00228 SECStatus
00229 SECMOD_LoadPKCS11Module(SECMODModule *mod) {
00230     PRLibrary *library = NULL;
00231     CK_C_GetFunctionList entry = NULL;
00232     char * full_name;
00233     CK_INFO info;
00234     CK_ULONG slotCount = 0;
00235     SECStatus rv;
00236     PRBool alreadyLoaded = PR_FALSE;
00237     char *disableUnload = NULL;
00238 
00239     if (mod->loaded) return SECSuccess;
00240 
00241     /* intenal modules get loaded from their internal list */
00242     if (mod->internal) {
00243        /* internal, statically get the C_GetFunctionList function */
00244        if (mod->isFIPS) {
00245            entry = (CK_C_GetFunctionList) FC_GetFunctionList;
00246        } else {
00247            entry = (CK_C_GetFunctionList) NSC_GetFunctionList;
00248        }
00249        if (mod->isModuleDB) {
00250            mod->moduleDBFunc = (void *) NSC_ModuleDBFunc;
00251        }
00252        if (mod->moduleDBOnly) {
00253            mod->loaded = PR_TRUE;
00254            return SECSuccess;
00255        }
00256     } else {
00257        /* Not internal, load the DLL and look up C_GetFunctionList */
00258        if (mod->dllName == NULL) {
00259            return SECFailure;
00260        }
00261 
00262 #ifdef notdef
00263        /* look up the library name */
00264        full_name = PR_GetLibraryName(PR_GetLibraryPath(),mod->dllName);
00265        if (full_name == NULL) {
00266            return SECFailure;
00267        }
00268 #else
00269        full_name = PORT_Strdup(mod->dllName);
00270 #endif
00271 
00272        /* load the library. If this succeeds, then we have to remember to
00273         * unload the library if anything goes wrong from here on out...
00274         */
00275        library = PR_LoadLibrary(full_name);
00276        mod->library = (void *)library;
00277        PORT_Free(full_name);
00278        if (library == NULL) {
00279            return SECFailure;
00280        }
00281 
00282        /*
00283         * now we need to get the entry point to find the function pointers
00284         */
00285        if (!mod->moduleDBOnly) {
00286            entry = (CK_C_GetFunctionList)
00287                      PR_FindSymbol(library, "C_GetFunctionList");
00288        }
00289        if (mod->isModuleDB) {
00290            mod->moduleDBFunc = (void *)
00291                      PR_FindSymbol(library, "NSS_ReturnModuleSpecData");
00292        }
00293        if (mod->moduleDBFunc == NULL) mod->isModuleDB = PR_FALSE;
00294        if (entry == NULL) {
00295            if (mod->isModuleDB) {
00296               mod->loaded = PR_TRUE;
00297               mod->moduleDBOnly = PR_TRUE;
00298               return SECSuccess;
00299            }
00300            PR_UnloadLibrary(library);
00301            return SECFailure;
00302        }
00303     }
00304 
00305     /*
00306      * We need to get the function list
00307      */
00308     if ((*entry)((CK_FUNCTION_LIST_PTR *)&mod->functionList) != CKR_OK) 
00309                                                         goto fail;
00310 
00311 #ifdef DEBUG_MODULE
00312     if (PR_TRUE) {
00313        modToDBG = PR_GetEnv("NSS_DEBUG_PKCS11_MODULE");
00314        if (modToDBG && strcmp(mod->commonName, modToDBG) == 0) {
00315            mod->functionList = (void *)nss_InsertDeviceLog(
00316                                   (CK_FUNCTION_LIST_PTR)mod->functionList);
00317        }
00318     }
00319 #endif
00320 
00321     mod->isThreadSafe = PR_TRUE;
00322 
00323     /* Now we initialize the module */
00324     rv = secmod_ModuleInit(mod, &alreadyLoaded);
00325     if (rv != SECSuccess) {
00326        goto fail;
00327     }
00328 
00329     /* check the version number */
00330     if (PK11_GETTAB(mod)->C_GetInfo(&info) != CKR_OK) goto fail2;
00331     if (info.cryptokiVersion.major != 2) goto fail2;
00332     /* all 2.0 are a priori *not* thread safe */
00333     if (info.cryptokiVersion.minor < 1) {
00334         if (!loadSingleThreadedModules) {
00335             PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
00336             goto fail2;
00337         } else {
00338             mod->isThreadSafe = PR_FALSE;
00339         }
00340     }
00341     mod->cryptokiVersion = info.cryptokiVersion;
00342 
00343     /* If we don't have a common name, get it from the PKCS 11 module */
00344     if ((mod->commonName == NULL) || (mod->commonName[0] == 0)) {
00345        mod->commonName = PK11_MakeString(mod->arena,NULL,
00346           (char *)info.libraryDescription, sizeof(info.libraryDescription));
00347        if (mod->commonName == NULL) goto fail2;
00348     }
00349     
00350 
00351     /* initialize the Slots */
00352     if (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &slotCount) == CKR_OK) {
00353        CK_SLOT_ID *slotIDs;
00354        int i;
00355        CK_RV crv;
00356 
00357        mod->slots = (PK11SlotInfo **)PORT_ArenaAlloc(mod->arena,
00358                                    sizeof(PK11SlotInfo *) * slotCount);
00359        if (mod->slots == NULL) goto fail2;
00360 
00361        slotIDs = (CK_SLOT_ID *) PORT_Alloc(sizeof(CK_SLOT_ID)*slotCount);
00362        if (slotIDs == NULL) {
00363            goto fail2;
00364        }  
00365        crv = PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, slotIDs, &slotCount);
00366        if (crv != CKR_OK) {
00367            PORT_Free(slotIDs);
00368            goto fail2;
00369        }
00370 
00371        /* Initialize each slot */
00372        for (i=0; i < (int)slotCount; i++) {
00373            mod->slots[i] = PK11_NewSlotInfo(mod);
00374            PK11_InitSlot(mod,slotIDs[i],mod->slots[i]);
00375            /* look down the slot info table */
00376            PK11_LoadSlotList(mod->slots[i],mod->slotInfo,mod->slotInfoCount);
00377            SECMOD_SetRootCerts(mod->slots[i],mod);
00378        }
00379        mod->slotCount = slotCount;
00380        mod->slotInfoCount = 0;
00381        PORT_Free(slotIDs);
00382     }
00383     
00384     mod->loaded = PR_TRUE;
00385     mod->moduleID = nextModuleID++;
00386     return SECSuccess;
00387 fail2:
00388     if (enforceAlreadyInitializedError || (!alreadyLoaded)) {
00389         PK11_GETTAB(mod)->C_Finalize(NULL);
00390     }
00391 fail:
00392     mod->functionList = NULL;
00393     disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
00394     if (library && !disableUnload) {
00395         PR_UnloadLibrary(library);
00396     }
00397     return SECFailure;
00398 }
00399 
00400 SECStatus
00401 SECMOD_UnloadModule(SECMODModule *mod) {
00402     PRLibrary *library;
00403     char *disableUnload = NULL;
00404 
00405     if (!mod->loaded) {
00406        return SECFailure;
00407     }
00408     if (finalizeModules) {
00409         if (!mod->moduleDBOnly) PK11_GETTAB(mod)->C_Finalize(NULL);
00410     }
00411     mod->moduleID = 0;
00412     mod->loaded = PR_FALSE;
00413     
00414     /* do we want the semantics to allow unloading the internal library?
00415      * if not, we should change this to SECFailure and move it above the
00416      * mod->loaded = PR_FALSE; */
00417     if (mod->internal) {
00418        return SECSuccess;
00419     }
00420 
00421     library = (PRLibrary *)mod->library;
00422     /* paranoia */
00423     if (library == NULL) {
00424        return SECFailure;
00425     }
00426 
00427     disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
00428     if (!disableUnload) {
00429         PR_UnloadLibrary(library);
00430     }
00431     return SECSuccess;
00432 }
00433 
00434 void
00435 nss_DumpModuleLog(void)
00436 {
00437 #ifdef DEBUG_MODULE
00438     if (modToDBG) {
00439        print_final_statistics();
00440     }
00441 #endif
00442 }