| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| /* |
| * The following code handles the storage of PKCS 11 modules used by the |
| * NSS. This file is written to abstract away how the modules are |
| * stored so we can deside that later. |
| */ |
| #include "sftkdb.h" |
| #include "sftkdbti.h" |
| #include "sdb.h" |
| #include "prsystem.h" |
| #include "prprf.h" |
| #include "prenv.h" |
| #include "lgglue.h" |
| #include "secerr.h" |
| #include "softoken.h" |
| |
| static LGOpenFunc legacy_glue_open = NULL; |
| static LGReadSecmodFunc legacy_glue_readSecmod = NULL; |
| static LGReleaseSecmodFunc legacy_glue_releaseSecmod = NULL; |
| static LGDeleteSecmodFunc legacy_glue_deleteSecmod = NULL; |
| static LGAddSecmodFunc legacy_glue_addSecmod = NULL; |
| static LGShutdownFunc legacy_glue_shutdown = NULL; |
| |
| /* |
| * The following 3 functions duplicate the work done by bl_LoadLibrary. |
| * We should make bl_LoadLibrary a global and replace the call to |
| * sftkdb_LoadLibrary(const char *libname) with it. |
| */ |
| #ifdef XP_UNIX |
| #include <unistd.h> |
| #define LG_MAX_LINKS 20 |
| static char * |
| sftkdb_resolvePath(const char *orig) |
| { |
| int count = 0; |
| int len = 0; |
| int ret = -1; |
| char *resolved = NULL; |
| char *source = NULL; |
| |
| len = 1025; /* MAX PATH +1*/ |
| if (strlen(orig) + 1 > len) { |
| /* PATH TOO LONG */ |
| return NULL; |
| } |
| resolved = PORT_Alloc(len); |
| if (!resolved) { |
| return NULL; |
| } |
| source = PORT_Alloc(len); |
| if (!source) { |
| goto loser; |
| } |
| PORT_Strcpy(source, orig); |
| /* Walk down all the links */ |
| while (count++ < LG_MAX_LINKS) { |
| char *tmp; |
| /* swap our previous sorce out with resolved */ |
| /* read it */ |
| ret = readlink(source, resolved, len - 1); |
| if (ret < 0) { |
| break; |
| } |
| resolved[ret] = 0; |
| tmp = source; |
| source = resolved; |
| resolved = tmp; |
| } |
| if (count > 1) { |
| ret = 0; |
| } |
| loser: |
| if (resolved) { |
| PORT_Free(resolved); |
| } |
| if (ret < 0) { |
| if (source) { |
| PORT_Free(source); |
| source = NULL; |
| } |
| } |
| return source; |
| } |
| |
| #endif |
| |
| static PRLibrary * |
| sftkdb_LoadFromPath(const char *path, const char *libname) |
| { |
| char *c; |
| int pathLen, nameLen, fullPathLen; |
| char *fullPathName = NULL; |
| PRLibSpec libSpec; |
| PRLibrary *lib = NULL; |
| |
| /* strip of our parent's library name */ |
| c = strrchr(path, PR_GetDirectorySeparator()); |
| if (!c) { |
| return NULL; /* invalid path */ |
| } |
| pathLen = (c - path) + 1; |
| nameLen = strlen(libname); |
| fullPathLen = pathLen + nameLen + 1; |
| fullPathName = (char *)PORT_Alloc(fullPathLen); |
| if (fullPathName == NULL) { |
| return NULL; /* memory allocation error */ |
| } |
| PORT_Memcpy(fullPathName, path, pathLen); |
| PORT_Memcpy(fullPathName + pathLen, libname, nameLen); |
| fullPathName[fullPathLen - 1] = 0; |
| |
| libSpec.type = PR_LibSpec_Pathname; |
| libSpec.value.pathname = fullPathName; |
| lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_LOCAL); |
| PORT_Free(fullPathName); |
| return lib; |
| } |
| |
| static PRLibrary * |
| sftkdb_LoadLibrary(const char *libname) |
| { |
| PRLibrary *lib = NULL; |
| PRFuncPtr fn_addr; |
| char *parentLibPath = NULL; |
| |
| fn_addr = (PRFuncPtr)&sftkdb_LoadLibrary; |
| parentLibPath = PR_GetLibraryFilePathname(SOFTOKEN_LIB_NAME, fn_addr); |
| |
| if (!parentLibPath) { |
| goto done; |
| } |
| |
| lib = sftkdb_LoadFromPath(parentLibPath, libname); |
| #ifdef XP_UNIX |
| /* handle symbolic link case */ |
| if (!lib) { |
| char *trueParentLibPath = sftkdb_resolvePath(parentLibPath); |
| if (!trueParentLibPath) { |
| goto done; |
| } |
| lib = sftkdb_LoadFromPath(trueParentLibPath, libname); |
| PORT_Free(trueParentLibPath); |
| } |
| #endif |
| |
| done: |
| if (parentLibPath) { |
| PORT_Free(parentLibPath); |
| } |
| |
| /* still couldn't load it, try the generic path */ |
| if (!lib) { |
| PRLibSpec libSpec; |
| libSpec.type = PR_LibSpec_Pathname; |
| libSpec.value.pathname = libname; |
| lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_LOCAL); |
| } |
| |
| return lib; |
| } |
| |
| /* |
| * stub files for legacy db's to be able to encrypt and decrypt |
| * various keys and attributes. |
| */ |
| static SECStatus |
| sftkdb_encrypt_stub(PLArenaPool *arena, SDB *sdb, SECItem *plainText, |
| SECItem **cipherText) |
| { |
| SFTKDBHandle *handle = sdb->app_private; |
| SECStatus rv; |
| |
| if (handle == NULL) { |
| return SECFailure; |
| } |
| |
| /* if we aren't the key handle, try the other handle */ |
| if (handle->type != SFTK_KEYDB_TYPE) { |
| handle = handle->peerDB; |
| } |
| |
| /* not a key handle */ |
| if (handle == NULL || handle->passwordLock == NULL) { |
| return SECFailure; |
| } |
| |
| PZ_Lock(handle->passwordLock); |
| if (handle->passwordKey.data == NULL) { |
| PZ_Unlock(handle->passwordLock); |
| /* PORT_SetError */ |
| return SECFailure; |
| } |
| |
| rv = sftkdb_EncryptAttribute(arena, |
| handle->newKey ? handle->newKey : &handle->passwordKey, |
| plainText, cipherText); |
| PZ_Unlock(handle->passwordLock); |
| |
| return rv; |
| } |
| |
| /* |
| * stub files for legacy db's to be able to encrypt and decrypt |
| * various keys and attributes. |
| */ |
| static SECStatus |
| sftkdb_decrypt_stub(SDB *sdb, SECItem *cipherText, SECItem **plainText) |
| { |
| SFTKDBHandle *handle = sdb->app_private; |
| SECStatus rv; |
| SECItem *oldKey = NULL; |
| |
| if (handle == NULL) { |
| return SECFailure; |
| } |
| |
| /* if we aren't th handle, try the other handle */ |
| oldKey = handle->oldKey; |
| if (handle->type != SFTK_KEYDB_TYPE) { |
| handle = handle->peerDB; |
| } |
| |
| /* not a key handle */ |
| if (handle == NULL || handle->passwordLock == NULL) { |
| return SECFailure; |
| } |
| |
| PZ_Lock(handle->passwordLock); |
| if (handle->passwordKey.data == NULL) { |
| PZ_Unlock(handle->passwordLock); |
| /* PORT_SetError */ |
| return SECFailure; |
| } |
| rv = sftkdb_DecryptAttribute(oldKey ? oldKey : &handle->passwordKey, |
| cipherText, plainText); |
| PZ_Unlock(handle->passwordLock); |
| |
| return rv; |
| } |
| |
| static const char *LEGACY_LIB_NAME = |
| SHLIB_PREFIX "nssdbm" SHLIB_VERSION "." SHLIB_SUFFIX; |
| /* |
| * 2 bools to tell us if we've check the legacy library successfully or |
| * not. Initialize on startup to false by the C BSS segment; |
| */ |
| static PRLibrary *legacy_glue_lib = NULL; |
| static SECStatus |
| sftkdbLoad_Legacy() |
| { |
| PRLibrary *lib = NULL; |
| LGSetCryptFunc setCryptFunction = NULL; |
| |
| if (legacy_glue_lib) { |
| return SECSuccess; |
| } |
| |
| lib = sftkdb_LoadLibrary(LEGACY_LIB_NAME); |
| if (lib == NULL) { |
| return SECFailure; |
| } |
| |
| legacy_glue_open = (LGOpenFunc)PR_FindFunctionSymbol(lib, "legacy_Open"); |
| legacy_glue_readSecmod = |
| (LGReadSecmodFunc)PR_FindFunctionSymbol(lib, "legacy_ReadSecmodDB"); |
| legacy_glue_releaseSecmod = |
| (LGReleaseSecmodFunc)PR_FindFunctionSymbol(lib, "legacy_ReleaseSecmodDBData"); |
| legacy_glue_deleteSecmod = |
| (LGDeleteSecmodFunc)PR_FindFunctionSymbol(lib, "legacy_DeleteSecmodDB"); |
| legacy_glue_addSecmod = |
| (LGAddSecmodFunc)PR_FindFunctionSymbol(lib, "legacy_AddSecmodDB"); |
| legacy_glue_shutdown = |
| (LGShutdownFunc)PR_FindFunctionSymbol(lib, "legacy_Shutdown"); |
| setCryptFunction = |
| (LGSetCryptFunc)PR_FindFunctionSymbol(lib, "legacy_SetCryptFunctions"); |
| |
| if (!legacy_glue_open || !legacy_glue_readSecmod || |
| !legacy_glue_releaseSecmod || !legacy_glue_deleteSecmod || |
| !legacy_glue_addSecmod || !setCryptFunction) { |
| PR_UnloadLibrary(lib); |
| return SECFailure; |
| } |
| |
| setCryptFunction(sftkdb_encrypt_stub, sftkdb_decrypt_stub); |
| legacy_glue_lib = lib; |
| return SECSuccess; |
| } |
| |
| CK_RV |
| sftkdbCall_open(const char *dir, const char *certPrefix, const char *keyPrefix, |
| int certVersion, int keyVersion, int flags, |
| SDB **certDB, SDB **keyDB) |
| { |
| SECStatus rv; |
| |
| rv = sftkdbLoad_Legacy(); |
| if (rv != SECSuccess) { |
| return CKR_GENERAL_ERROR; |
| } |
| if (!legacy_glue_open) { |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return SECFailure; |
| } |
| return (*legacy_glue_open)(dir, certPrefix, keyPrefix, |
| certVersion, keyVersion, |
| flags, certDB, keyDB); |
| } |
| |
| char ** |
| sftkdbCall_ReadSecmodDB(const char *appName, const char *filename, |
| const char *dbname, char *params, PRBool rw) |
| { |
| SECStatus rv; |
| |
| rv = sftkdbLoad_Legacy(); |
| if (rv != SECSuccess) { |
| return NULL; |
| } |
| if (!legacy_glue_readSecmod) { |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return NULL; |
| } |
| return (*legacy_glue_readSecmod)(appName, filename, dbname, params, rw); |
| } |
| |
| SECStatus |
| sftkdbCall_ReleaseSecmodDBData(const char *appName, |
| const char *filename, const char *dbname, |
| char **moduleSpecList, PRBool rw) |
| { |
| SECStatus rv; |
| |
| rv = sftkdbLoad_Legacy(); |
| if (rv != SECSuccess) { |
| return rv; |
| } |
| if (!legacy_glue_releaseSecmod) { |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return SECFailure; |
| } |
| return (*legacy_glue_releaseSecmod)(appName, filename, dbname, |
| moduleSpecList, rw); |
| } |
| |
| SECStatus |
| sftkdbCall_DeleteSecmodDB(const char *appName, |
| const char *filename, const char *dbname, |
| char *args, PRBool rw) |
| { |
| SECStatus rv; |
| |
| rv = sftkdbLoad_Legacy(); |
| if (rv != SECSuccess) { |
| return rv; |
| } |
| if (!legacy_glue_deleteSecmod) { |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return SECFailure; |
| } |
| return (*legacy_glue_deleteSecmod)(appName, filename, dbname, args, rw); |
| } |
| |
| SECStatus |
| sftkdbCall_AddSecmodDB(const char *appName, |
| const char *filename, const char *dbname, |
| char *module, PRBool rw) |
| { |
| SECStatus rv; |
| |
| rv = sftkdbLoad_Legacy(); |
| if (rv != SECSuccess) { |
| return rv; |
| } |
| if (!legacy_glue_addSecmod) { |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return SECFailure; |
| } |
| return (*legacy_glue_addSecmod)(appName, filename, dbname, module, rw); |
| } |
| |
| CK_RV |
| sftkdbCall_Shutdown(void) |
| { |
| CK_RV crv = CKR_OK; |
| char *disableUnload = NULL; |
| if (!legacy_glue_lib) { |
| return CKR_OK; |
| } |
| if (legacy_glue_shutdown) { |
| #ifdef NO_FORK_CHECK |
| PRBool parentForkedAfterC_Initialize = PR_FALSE; |
| #endif |
| crv = (*legacy_glue_shutdown)(parentForkedAfterC_Initialize); |
| } |
| disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD"); |
| if (!disableUnload) { |
| PR_UnloadLibrary(legacy_glue_lib); |
| } |
| legacy_glue_lib = NULL; |
| legacy_glue_open = NULL; |
| legacy_glue_readSecmod = NULL; |
| legacy_glue_releaseSecmod = NULL; |
| legacy_glue_deleteSecmod = NULL; |
| legacy_glue_addSecmod = NULL; |
| return crv; |
| } |